[MERGE]with main branch
bzr revid: jpr@tinyerp.com-20140327063821-n85232vzt06v8mo9
This commit is contained in:
commit
6e9c73de0d
|
@ -58,6 +58,7 @@ The kernel of OpenERP, needed for all installation.
|
||||||
'ir/osv_memory_autovacuum.xml',
|
'ir/osv_memory_autovacuum.xml',
|
||||||
'ir/ir_model_report.xml',
|
'ir/ir_model_report.xml',
|
||||||
'ir/ir_logging_view.xml',
|
'ir/ir_logging_view.xml',
|
||||||
|
'ir/ir_qweb.xml',
|
||||||
'workflow/workflow_view.xml',
|
'workflow/workflow_view.xml',
|
||||||
'module/module_view.xml',
|
'module/module_view.xml',
|
||||||
'module/module_data.xml',
|
'module/module_data.xml',
|
||||||
|
|
|
@ -141,12 +141,12 @@ class ir_actions_report_xml(osv.osv):
|
||||||
'model': fields.char('Model', required=True),
|
'model': fields.char('Model', required=True),
|
||||||
'report_type': fields.selection([('qweb-pdf', 'PDF'),
|
'report_type': fields.selection([('qweb-pdf', 'PDF'),
|
||||||
('qweb-html', 'HTML'),
|
('qweb-html', 'HTML'),
|
||||||
('other', 'Other'),
|
('controller', 'Controller'),
|
||||||
('pdf', 'RML pdf (deprecated)'),
|
('pdf', 'RML pdf (deprecated)'),
|
||||||
('sxw', 'RML sxw (deprecated)'),
|
('sxw', 'RML sxw (deprecated)'),
|
||||||
('webkit', 'Webkit (deprecated)'),
|
('webkit', 'Webkit (deprecated)'),
|
||||||
], 'Report Type', required=True, help="PDF will use wkhtmltopdf to render html to pdf, HTML will directly show html, Other will force download the controller output keeping the MIME type."),
|
], 'Report Type', required=True, help="HTML will open the report directly in your browser, PDF will use wkhtmltopdf to render the HTML into a PDF file and let you download it, Controller allows you to define the url of a custom controller outputting any kind of report."),
|
||||||
'report_name': fields.char('Controller Name', required=True, help="URL of the report will be /report/<controller name>/<ids>, the default controller also use this field to get the name of the qweb ir.ui.view to render. For RML reports, this is the LocalService name."),
|
'report_name': fields.char('Template Name', required=True, help="For QWeb reports, name of the template used in the rendering. The method 'render_html' of the model 'report.template_name' will be called (if any) to give the html. For RML reports, this is the LocalService name."),
|
||||||
'groups_id': fields.many2many('res.groups', 'res_groups_report_rel', 'uid', 'gid', 'Groups'),
|
'groups_id': fields.many2many('res.groups', 'res_groups_report_rel', 'uid', 'gid', 'Groups'),
|
||||||
|
|
||||||
# options
|
# options
|
||||||
|
@ -163,7 +163,7 @@ class ir_actions_report_xml(osv.osv):
|
||||||
'report_xsl': fields.char('XSL Path'),
|
'report_xsl': fields.char('XSL Path'),
|
||||||
'report_xml': fields.char('XML Path'),
|
'report_xml': fields.char('XML Path'),
|
||||||
|
|
||||||
'report_rml': fields.char('Main Report File Path', help="The path to the main report file (depending on Report Type) or NULL if the content is in another data field"),
|
'report_rml': fields.char('Main Report File Path/controller', help="The path to the main report file/controller (depending on Report Type) or NULL if the content is in another data field"),
|
||||||
'report_file': fields.related('report_rml', type="char", required=False, readonly=False, string='Report File', help="The path to the main report file (depending on Report Type) or NULL if the content is in another field", store=True),
|
'report_file': fields.related('report_rml', type="char", required=False, readonly=False, string='Report File', help="The path to the main report file (depending on Report Type) or NULL if the content is in another field", store=True),
|
||||||
|
|
||||||
'report_sxw': fields.function(_report_sxw, type='char', string='SXW Path'),
|
'report_sxw': fields.function(_report_sxw, type='char', string='SXW Path'),
|
||||||
|
|
|
@ -59,12 +59,13 @@
|
||||||
<field name="name"/>
|
<field name="name"/>
|
||||||
<field name="model"/>
|
<field name="model"/>
|
||||||
<field name="report_type"/>
|
<field name="report_type"/>
|
||||||
<field name="report_name"/>
|
<field name="report_name" attrs="{'invisible':[('report_type','=', 'controller')]}"/>
|
||||||
|
<field name="report_rml" attrs="{'invisible':[('report_type','!=', 'controller')]}"/>
|
||||||
</group>
|
</group>
|
||||||
<group>
|
<group>
|
||||||
<field name="multi"/>
|
<field name="multi"/>
|
||||||
<field name="attachment_use"/>
|
<field name="attachment_use" attrs="{'invisible':[('report_type','=', 'controller')]}"/>
|
||||||
<field name="attachment"/>
|
<field name="attachment" attrs="{'invisible':[('report_type','=', 'controller')]}"/>
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
<notebook>
|
<notebook>
|
||||||
|
|
|
@ -69,7 +69,7 @@ class ir_attachment(osv.osv):
|
||||||
|
|
||||||
@tools.ormcache()
|
@tools.ormcache()
|
||||||
def _filestore(self, cr, uid, context=None):
|
def _filestore(self, cr, uid, context=None):
|
||||||
return os.path.join(tools.config['data_dir'], 'filestore', cr.dbname)
|
return tools.config.filestore(cr.dbname)
|
||||||
|
|
||||||
# 'data' field implementation
|
# 'data' field implementation
|
||||||
def _full_path(self, cr, uid, location, path):
|
def _full_path(self, cr, uid, location, path):
|
||||||
|
|
|
@ -808,6 +808,38 @@ class RelativeDatetimeConverter(osv.AbstractModel):
|
||||||
return babel.dates.format_timedelta(
|
return babel.dates.format_timedelta(
|
||||||
value - reference, add_direction=True, locale=locale)
|
value - reference, add_direction=True, locale=locale)
|
||||||
|
|
||||||
|
|
||||||
|
class Contact(orm.AbstractModel):
|
||||||
|
_name = 'ir.qweb.field.contact'
|
||||||
|
_inherit = 'ir.qweb.field.many2one'
|
||||||
|
|
||||||
|
def record_to_html(self, cr, uid, field_name, record, column, options=None, context=None):
|
||||||
|
opf = options.get('fields') or ["name", "address", "phone", "mobile", "fax", "email"]
|
||||||
|
|
||||||
|
if not getattr(record, field_name):
|
||||||
|
return None
|
||||||
|
|
||||||
|
id = getattr(record, field_name).id
|
||||||
|
field_browse = self.pool[column._obj].browse(cr, openerp.SUPERUSER_ID, id, context={"show_address": True})
|
||||||
|
value = werkzeug.utils.escape( field_browse.name_get()[0][1] )
|
||||||
|
|
||||||
|
val = {
|
||||||
|
'name': value.split("\n")[0],
|
||||||
|
'address': werkzeug.utils.escape("\n".join(value.split("\n")[1:])),
|
||||||
|
'phone': field_browse.phone,
|
||||||
|
'mobile': field_browse.mobile,
|
||||||
|
'fax': field_browse.fax,
|
||||||
|
'city': field_browse.city,
|
||||||
|
'country_id': field_browse.country_id and field_browse.country_id.name_get()[0][1],
|
||||||
|
'email': field_browse.email,
|
||||||
|
'fields': opf,
|
||||||
|
'options': options
|
||||||
|
}
|
||||||
|
|
||||||
|
html = self.pool["ir.ui.view"].render(cr, uid, "base.contact", val, engine='ir.qweb', context=context).decode('utf8')
|
||||||
|
|
||||||
|
return HTMLSafe(html)
|
||||||
|
|
||||||
class HTMLSafe(object):
|
class HTMLSafe(object):
|
||||||
""" HTMLSafe string wrapper, Werkzeug's escape() has special handling for
|
""" HTMLSafe string wrapper, Werkzeug's escape() has special handling for
|
||||||
objects with a ``__html__`` methods but AFAIK does not provide any such
|
objects with a ``__html__`` methods but AFAIK does not provide any such
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
<openerp>
|
||||||
|
<data>
|
||||||
|
<template id="contact">
|
||||||
|
<address t-ignore="true" class="mb0" itemscope="itemscope" itemtype="http://schema.org/Organization">
|
||||||
|
<div t-att-class="'name' not in fields and 'css_non_editable_mode_hidden'"><span itemprop="name" t-esc="name"/></div>
|
||||||
|
<div itemprop="address" itemscope="itemscope" itemtype="http://schema.org/PostalAddress">
|
||||||
|
<div t-if="address and 'address' in fields" class='css_editable_mode_hidden'>
|
||||||
|
<i t-if="not options.get('no_marker')" class='fa fa-map-marker'/> <span itemprop="streetAddress" t-raw="address.replace('\n', options.get('no_tag_br') and ', ' or ('<br/>%s' % ('' if options.get('no_marker') else '&nbsp; &nbsp; ')))"/>
|
||||||
|
</div>
|
||||||
|
<div t-if="city and 'city' in fields" class='css_editable_mode_hidden'>
|
||||||
|
<i t-if="not options.get('no_marker')" class='fa fa-map-marker'/> <span itemprop="addressLocality" t-raw="city"/>, <span itemprop="addressCountry" t-raw="country_id"/>
|
||||||
|
</div>
|
||||||
|
<div t-if="phone and 'phone' in fields" class='css_editable_mode_hidden'><i t-if="not options.get('no_marker')" class='fa fa-phone'/> <span itemprop="telephone" t-esc="phone"/></div>
|
||||||
|
<div t-if="mobile and 'mobile' in fields" class='css_editable_mode_hidden'><i t-if="not options.get('no_marker')" class='fa fa-mobile-phone'/> <span itemprop="telephone" t-esc="mobile"/></div>
|
||||||
|
<div t-if="fax and 'fax' in fields" class='css_editable_mode_hidden'><i t-if="not options.get('no_marker')" class='fa fa-file-text-o'/> <span itemprop="faxNumber" t-esc="fax"/></div>
|
||||||
|
<div t-if="email and 'email' in fields" class='css_editable_mode_hidden'><i t-if="not options.get('no_marker')" class='fa fa-envelope'/> <span itemprop="email" t-esc="email"/></div>
|
||||||
|
</div>
|
||||||
|
</address>
|
||||||
|
</template>
|
||||||
|
</data>
|
||||||
|
</openerp>
|
|
@ -493,6 +493,10 @@ class res_config_settings(osv.osv_memory, res_config_module_installation_mixin):
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def execute(self, cr, uid, ids, context=None):
|
def execute(self, cr, uid, ids, context=None):
|
||||||
|
if context is None:
|
||||||
|
context = {}
|
||||||
|
|
||||||
|
context = dict(context, active_test=False)
|
||||||
if uid != SUPERUSER_ID and not self.pool['res.users'].has_group(cr, uid, 'base.group_erp_manager'):
|
if uid != SUPERUSER_ID and not self.pool['res.users'].has_group(cr, uid, 'base.group_erp_manager'):
|
||||||
raise openerp.exceptions.AccessError(_("Only administrators can change the settings"))
|
raise openerp.exceptions.AccessError(_("Only administrators can change the settings"))
|
||||||
|
|
||||||
|
@ -500,7 +504,7 @@ class res_config_settings(osv.osv_memory, res_config_module_installation_mixin):
|
||||||
ir_module = self.pool['ir.module.module']
|
ir_module = self.pool['ir.module.module']
|
||||||
res_groups = self.pool['res.groups']
|
res_groups = self.pool['res.groups']
|
||||||
|
|
||||||
classified = self._get_classified_fields(cr, uid, context)
|
classified = self._get_classified_fields(cr, uid, context=context)
|
||||||
|
|
||||||
config = self.browse(cr, uid, ids[0], context)
|
config = self.browse(cr, uid, ids[0], context)
|
||||||
|
|
||||||
|
|
|
@ -586,7 +586,7 @@ class groups_implied(osv.osv):
|
||||||
res = super(groups_implied, self).write(cr, uid, ids, values, context)
|
res = super(groups_implied, self).write(cr, uid, ids, values, context)
|
||||||
if values.get('users') or values.get('implied_ids'):
|
if values.get('users') or values.get('implied_ids'):
|
||||||
# add all implied groups (to all users of each group)
|
# add all implied groups (to all users of each group)
|
||||||
for g in self.browse(cr, uid, ids):
|
for g in self.browse(cr, uid, ids, context=context):
|
||||||
gids = map(int, g.trans_implied_ids)
|
gids = map(int, g.trans_implied_ids)
|
||||||
vals = {'users': [(4, u.id) for u in g.users]}
|
vals = {'users': [(4, u.id) for u in g.users]}
|
||||||
super(groups_implied, self).write(cr, uid, gids, vals, context)
|
super(groups_implied, self).write(cr, uid, gids, vals, context)
|
||||||
|
|
|
@ -127,7 +127,6 @@ def main(args):
|
||||||
check_root_user()
|
check_root_user()
|
||||||
openerp.tools.config.parse_config(args)
|
openerp.tools.config.parse_config(args)
|
||||||
check_postgres_user()
|
check_postgres_user()
|
||||||
openerp.netsvc.init_logger()
|
|
||||||
report_configuration()
|
report_configuration()
|
||||||
|
|
||||||
config = openerp.tools.config
|
config = openerp.tools.config
|
||||||
|
|
|
@ -36,6 +36,7 @@ import werkzeug.wsgi
|
||||||
|
|
||||||
import openerp
|
import openerp
|
||||||
from openerp.service import security, model as service_model
|
from openerp.service import security, model as service_model
|
||||||
|
from openerp.tools.func import lazy_property
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -286,7 +287,7 @@ class WebRequest(object):
|
||||||
def checked_call(___dbname, *a, **kw):
|
def checked_call(___dbname, *a, **kw):
|
||||||
# The decorator can call us more than once if there is an database error. In this
|
# The decorator can call us more than once if there is an database error. In this
|
||||||
# case, the request cursor is unusable. Rollback transaction to create a new one.
|
# case, the request cursor is unusable. Rollback transaction to create a new one.
|
||||||
if self._cr:
|
if self._cr and not openerp.tools.config['test_enable']:
|
||||||
self._cr.rollback()
|
self._cr.rollback()
|
||||||
return self.endpoint(*a, **kw)
|
return self.endpoint(*a, **kw)
|
||||||
|
|
||||||
|
@ -1077,22 +1078,25 @@ class Root(object):
|
||||||
path = openerp.tools.config.session_dir
|
path = openerp.tools.config.session_dir
|
||||||
_logger.debug('HTTP sessions stored in: %s', path)
|
_logger.debug('HTTP sessions stored in: %s', path)
|
||||||
self.session_store = werkzeug.contrib.sessions.FilesystemSessionStore(path, session_class=OpenERPSession)
|
self.session_store = werkzeug.contrib.sessions.FilesystemSessionStore(path, session_class=OpenERPSession)
|
||||||
|
self._loaded = False
|
||||||
|
|
||||||
# TODO should we move this to ir.http so that only configured modules are served ?
|
@lazy_property
|
||||||
_logger.info("HTTP Configuring static files")
|
def nodb_routing_map(self):
|
||||||
self.load_addons()
|
|
||||||
|
|
||||||
_logger.info("Generating nondb routing")
|
_logger.info("Generating nondb routing")
|
||||||
self.nodb_routing_map = routing_map([''] + openerp.conf.server_wide_modules, True)
|
return routing_map([''] + openerp.conf.server_wide_modules, True)
|
||||||
|
|
||||||
def __call__(self, environ, start_response):
|
def __call__(self, environ, start_response):
|
||||||
""" Handle a WSGI request
|
""" Handle a WSGI request
|
||||||
"""
|
"""
|
||||||
|
if not self._loaded:
|
||||||
|
self._loaded = True
|
||||||
|
self.load_addons()
|
||||||
return self.dispatch(environ, start_response)
|
return self.dispatch(environ, start_response)
|
||||||
|
|
||||||
def load_addons(self):
|
def load_addons(self):
|
||||||
""" Load all addons from addons patch containg static files and
|
""" Load all addons from addons patch containg static files and
|
||||||
controllers and configure them. """
|
controllers and configure them. """
|
||||||
|
# TODO should we move this to ir.http so that only configured modules are served ?
|
||||||
statics = {}
|
statics = {}
|
||||||
|
|
||||||
for addons_path in openerp.modules.module.ad_paths:
|
for addons_path in openerp.modules.module.ad_paths:
|
||||||
|
@ -1106,12 +1110,16 @@ class Root(object):
|
||||||
_logger.debug("Loading %s", module)
|
_logger.debug("Loading %s", module)
|
||||||
if 'openerp.addons' in sys.modules:
|
if 'openerp.addons' in sys.modules:
|
||||||
m = __import__('openerp.addons.' + module)
|
m = __import__('openerp.addons.' + module)
|
||||||
|
else:
|
||||||
|
m = None
|
||||||
addons_module[module] = m
|
addons_module[module] = m
|
||||||
addons_manifest[module] = manifest
|
addons_manifest[module] = manifest
|
||||||
statics['/%s/static' % module] = path_static
|
statics['/%s/static' % module] = path_static
|
||||||
|
|
||||||
app = werkzeug.wsgi.SharedDataMiddleware(self.dispatch, statics)
|
if statics:
|
||||||
self.dispatch = DisableCacheMiddleware(app)
|
_logger.info("HTTP Configuring static files")
|
||||||
|
app = werkzeug.wsgi.SharedDataMiddleware(self.dispatch, statics)
|
||||||
|
self.dispatch = DisableCacheMiddleware(app)
|
||||||
|
|
||||||
def setup_session(self, httprequest):
|
def setup_session(self, httprequest):
|
||||||
# recover or create session
|
# recover or create session
|
||||||
|
@ -1284,11 +1292,8 @@ class CommonController(Controller):
|
||||||
""" Method used by client APIs to contact OpenERP. """
|
""" Method used by client APIs to contact OpenERP. """
|
||||||
return dispatch_rpc(service, method, args)
|
return dispatch_rpc(service, method, args)
|
||||||
|
|
||||||
root = None
|
# register main wsgi handler
|
||||||
|
root = Root()
|
||||||
def wsgi_postload():
|
openerp.service.wsgi_server.register_wsgi_handler(root)
|
||||||
global root
|
|
||||||
root = Root()
|
|
||||||
openerp.service.wsgi_server.register_wsgi_handler(root)
|
|
||||||
|
|
||||||
# vim:et:ts=4:sw=4:
|
# vim:et:ts=4:sw=4:
|
||||||
|
|
|
@ -42,6 +42,7 @@ from openerp import SUPERUSER_ID
|
||||||
from openerp.tools.translate import _
|
from openerp.tools.translate import _
|
||||||
from openerp.modules.module import initialize_sys_path, \
|
from openerp.modules.module import initialize_sys_path, \
|
||||||
load_openerp_module, init_module_models, adapt_version
|
load_openerp_module, init_module_models, adapt_version
|
||||||
|
from module import runs_post_install
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
_test_logger = logging.getLogger('openerp.tests')
|
_test_logger = logging.getLogger('openerp.tests')
|
||||||
|
@ -425,8 +426,13 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
|
||||||
for model in registry.models.values():
|
for model in registry.models.values():
|
||||||
model._register_hook(cr)
|
model._register_hook(cr)
|
||||||
|
|
||||||
|
# STEP 9: Run the post-install tests
|
||||||
|
cr.commit()
|
||||||
|
if openerp.tools.config['test_enable']:
|
||||||
|
cr.execute("SELECT name FROM ir_module_module WHERE state='installed'")
|
||||||
|
for module_name in cr.fetchall():
|
||||||
|
report.record_result(openerp.modules.module.run_unit_tests(module_name[0], cr.dbname, position=runs_post_install))
|
||||||
finally:
|
finally:
|
||||||
cr.close()
|
cr.close()
|
||||||
|
|
||||||
|
|
||||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||||
|
|
|
@ -356,30 +356,6 @@ class TestStream(object):
|
||||||
|
|
||||||
current_test = None
|
current_test = None
|
||||||
|
|
||||||
def run_unit_tests(module_name, dbname):
|
|
||||||
"""
|
|
||||||
:returns: ``True`` if all of ``module_name``'s tests succeeded, ``False``
|
|
||||||
if any of them failed.
|
|
||||||
:rtype: bool
|
|
||||||
"""
|
|
||||||
global current_test
|
|
||||||
current_test = module_name
|
|
||||||
mods = get_test_modules(module_name)
|
|
||||||
r = True
|
|
||||||
for m in mods:
|
|
||||||
tests = unwrap_suite(unittest2.TestLoader().loadTestsFromModule(m))
|
|
||||||
suite = unittest2.TestSuite(itertools.ifilter(runs_at_install, tests))
|
|
||||||
_logger.info('running %s tests.', m.__name__)
|
|
||||||
|
|
||||||
result = unittest2.TextTestRunner(verbosity=2, stream=TestStream(m.__name__)).run(suite)
|
|
||||||
|
|
||||||
if not result.wasSuccessful():
|
|
||||||
r = False
|
|
||||||
_logger.error("Module %s: %d failures, %d errors",
|
|
||||||
module_name, len(result.failures), len(result.errors))
|
|
||||||
current_test = None
|
|
||||||
return r
|
|
||||||
|
|
||||||
def runs_at(test, hook, default):
|
def runs_at(test, hook, default):
|
||||||
# by default, tests do not run post install
|
# by default, tests do not run post install
|
||||||
test_runs = getattr(test, hook, default)
|
test_runs = getattr(test, hook, default)
|
||||||
|
@ -396,6 +372,30 @@ def runs_at(test, hook, default):
|
||||||
runs_at_install = functools.partial(runs_at, hook='at_install', default=True)
|
runs_at_install = functools.partial(runs_at, hook='at_install', default=True)
|
||||||
runs_post_install = functools.partial(runs_at, hook='post_install', default=False)
|
runs_post_install = functools.partial(runs_at, hook='post_install', default=False)
|
||||||
|
|
||||||
|
def run_unit_tests(module_name, dbname, position=runs_at_install):
|
||||||
|
"""
|
||||||
|
:returns: ``True`` if all of ``module_name``'s tests succeeded, ``False``
|
||||||
|
if any of them failed.
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
global current_test
|
||||||
|
current_test = module_name
|
||||||
|
mods = get_test_modules(module_name)
|
||||||
|
r = True
|
||||||
|
for m in mods:
|
||||||
|
tests = unwrap_suite(unittest2.TestLoader().loadTestsFromModule(m))
|
||||||
|
suite = unittest2.TestSuite(itertools.ifilter(position, tests))
|
||||||
|
_logger.info('running %s tests.', m.__name__)
|
||||||
|
|
||||||
|
result = unittest2.TextTestRunner(verbosity=2, stream=TestStream(m.__name__)).run(suite)
|
||||||
|
|
||||||
|
if not result.wasSuccessful():
|
||||||
|
r = False
|
||||||
|
_logger.error("Module %s: %d failures, %d errors",
|
||||||
|
module_name, len(result.failures), len(result.errors))
|
||||||
|
current_test = None
|
||||||
|
return r
|
||||||
|
|
||||||
def unwrap_suite(test):
|
def unwrap_suite(test):
|
||||||
"""
|
"""
|
||||||
Attempts to unpack testsuites (holding suites or cases) in order to
|
Attempts to unpack testsuites (holding suites or cases) in order to
|
||||||
|
|
|
@ -126,6 +126,11 @@ def exp_duplicate_database(db_original_name, db_name):
|
||||||
with closing(db.cursor()) as cr:
|
with closing(db.cursor()) as cr:
|
||||||
cr.autocommit(True) # avoid transaction block
|
cr.autocommit(True) # avoid transaction block
|
||||||
cr.execute("""CREATE DATABASE "%s" ENCODING 'unicode' TEMPLATE "%s" """ % (db_name, db_original_name))
|
cr.execute("""CREATE DATABASE "%s" ENCODING 'unicode' TEMPLATE "%s" """ % (db_name, db_original_name))
|
||||||
|
|
||||||
|
from_fs = openerp.tools.config.filestore(db_original_name)
|
||||||
|
to_fs = openerp.tools.config.filestore(db_name)
|
||||||
|
if os.path.exists(from_fs) and not os.path.exists(to_fs):
|
||||||
|
shutil.copy(from_fs, to_fs)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def exp_get_progress(id):
|
def exp_get_progress(id):
|
||||||
|
@ -178,6 +183,10 @@ def exp_drop(db_name):
|
||||||
raise Exception("Couldn't drop database %s: %s" % (db_name, e))
|
raise Exception("Couldn't drop database %s: %s" % (db_name, e))
|
||||||
else:
|
else:
|
||||||
_logger.info('DROP DB: %s', db_name)
|
_logger.info('DROP DB: %s', db_name)
|
||||||
|
|
||||||
|
fs = openerp.tools.config.filestore(db_name)
|
||||||
|
if os.path.exists(fs):
|
||||||
|
shutil.rmtree(fs)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _set_pg_password_in_environment(func):
|
def _set_pg_password_in_environment(func):
|
||||||
|
@ -328,6 +337,11 @@ def exp_rename(old_name, new_name):
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
_logger.error('RENAME DB: %s -> %s failed:\n%s', old_name, new_name, e)
|
_logger.error('RENAME DB: %s -> %s failed:\n%s', old_name, new_name, e)
|
||||||
raise Exception("Couldn't rename database %s to %s: %s" % (old_name, new_name, e))
|
raise Exception("Couldn't rename database %s to %s: %s" % (old_name, new_name, e))
|
||||||
|
|
||||||
|
old_fs = openerp.tools.config.filestore(old_name)
|
||||||
|
new_fs = openerp.tools.config.filestore(new_name)
|
||||||
|
if os.path.exists(old_fs) and not os.path.exists(new_fs):
|
||||||
|
shutil.move(old_fs, new_fs)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@openerp.tools.mute_logger('openerp.sql_db')
|
@openerp.tools.mute_logger('openerp.sql_db')
|
||||||
|
|
|
@ -1,178 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
# Copyright P. Christeas <p_christ@hol.gr> 2008-2010
|
|
||||||
# Copyright 2010 OpenERP SA. (http://www.openerp.com)
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# WARNING: This program as such is intended to be used by professional
|
|
||||||
# programmers who take the whole responsibility of assessing all potential
|
|
||||||
# consequences resulting from its eventual inadequacies and bugs
|
|
||||||
# End users who are looking for a ready-to-use solution with commercial
|
|
||||||
# guarantees and support are strongly advised to contract a Free Software
|
|
||||||
# Service Company
|
|
||||||
#
|
|
||||||
# This program is Free Software; you can redistribute it and/or
|
|
||||||
# modify it under the terms of the GNU General Public License
|
|
||||||
# as published by the Free Software Foundation; either version 2
|
|
||||||
# of the License, or (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program; if not, write to the Free Software
|
|
||||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
|
|
||||||
""" This module offers the family of HTTP-based servers. These are not a single
|
|
||||||
class/functionality, but a set of network stack layers, implementing
|
|
||||||
extendable HTTP protocols.
|
|
||||||
|
|
||||||
The OpenERP server defines a single instance of a HTTP server, listening at
|
|
||||||
the standard 8069, 8071 ports (well, it is 2 servers, and ports are
|
|
||||||
configurable, of course). This "single" server then uses a `MultiHTTPHandler`
|
|
||||||
to dispatch requests to the appropriate channel protocol, like the XML-RPC,
|
|
||||||
static HTTP, DAV or other.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import base64
|
|
||||||
import posixpath
|
|
||||||
import urllib
|
|
||||||
import os
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from websrv_lib import *
|
|
||||||
import openerp.tools as tools
|
|
||||||
|
|
||||||
try:
|
|
||||||
import fcntl
|
|
||||||
except ImportError:
|
|
||||||
fcntl = None
|
|
||||||
|
|
||||||
try:
|
|
||||||
from ssl import SSLError
|
|
||||||
except ImportError:
|
|
||||||
class SSLError(Exception): pass
|
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
# TODO delete this for 6.2, it is still needed for 6.1.
|
|
||||||
class HttpLogHandler:
|
|
||||||
""" helper class for uniform log handling
|
|
||||||
Please define self._logger at each class that is derived from this
|
|
||||||
"""
|
|
||||||
_logger = None
|
|
||||||
|
|
||||||
def log_message(self, format, *args):
|
|
||||||
self._logger.debug(format % args) # todo: perhaps other level
|
|
||||||
|
|
||||||
def log_error(self, format, *args):
|
|
||||||
self._logger.error(format % args)
|
|
||||||
|
|
||||||
def log_exception(self, format, *args):
|
|
||||||
self._logger.exception(format, *args)
|
|
||||||
|
|
||||||
def log_request(self, code='-', size='-'):
|
|
||||||
self._logger.debug('"%s" %s %s',
|
|
||||||
self.requestline, str(code), str(size))
|
|
||||||
|
|
||||||
class StaticHTTPHandler(HttpLogHandler, FixSendError, HttpOptions, HTTPHandler):
|
|
||||||
_logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
_HTTP_OPTIONS = { 'Allow': ['OPTIONS', 'GET', 'HEAD'] }
|
|
||||||
|
|
||||||
def __init__(self,request, client_address, server):
|
|
||||||
HTTPHandler.__init__(self,request,client_address,server)
|
|
||||||
document_root = tools.config.get('static_http_document_root', False)
|
|
||||||
assert document_root, "Please specify static_http_document_root in configuration, or disable static-httpd!"
|
|
||||||
self.__basepath = document_root
|
|
||||||
|
|
||||||
def translate_path(self, path):
|
|
||||||
"""Translate a /-separated PATH to the local filename syntax.
|
|
||||||
|
|
||||||
Components that mean special things to the local file system
|
|
||||||
(e.g. drive or directory names) are ignored. (XXX They should
|
|
||||||
probably be diagnosed.)
|
|
||||||
|
|
||||||
"""
|
|
||||||
# abandon query parameters
|
|
||||||
path = path.split('?',1)[0]
|
|
||||||
path = path.split('#',1)[0]
|
|
||||||
path = posixpath.normpath(urllib.unquote(path))
|
|
||||||
words = path.split('/')
|
|
||||||
words = filter(None, words)
|
|
||||||
path = self.__basepath
|
|
||||||
for word in words:
|
|
||||||
if word in (os.curdir, os.pardir): continue
|
|
||||||
path = os.path.join(path, word)
|
|
||||||
return path
|
|
||||||
|
|
||||||
def init_static_http():
|
|
||||||
if not tools.config.get('static_http_enable', False):
|
|
||||||
return
|
|
||||||
|
|
||||||
document_root = tools.config.get('static_http_document_root', False)
|
|
||||||
assert document_root, "Document root must be specified explicitly to enable static HTTP service (option --static-http-document-root)"
|
|
||||||
|
|
||||||
base_path = tools.config.get('static_http_url_prefix', '/')
|
|
||||||
|
|
||||||
reg_http_service(base_path, StaticHTTPHandler)
|
|
||||||
|
|
||||||
_logger.info("Registered HTTP dir %s for %s", document_root, base_path)
|
|
||||||
|
|
||||||
import security
|
|
||||||
|
|
||||||
class OpenERPAuthProvider(AuthProvider):
|
|
||||||
""" Require basic authentication."""
|
|
||||||
def __init__(self,realm='OpenERP User'):
|
|
||||||
self.realm = realm
|
|
||||||
self.auth_creds = {}
|
|
||||||
self.auth_tries = 0
|
|
||||||
self.last_auth = None
|
|
||||||
|
|
||||||
def authenticate(self, db, user, passwd, client_address):
|
|
||||||
try:
|
|
||||||
uid = security.login(db,user,passwd)
|
|
||||||
if uid is False:
|
|
||||||
return False
|
|
||||||
return user, passwd, db, uid
|
|
||||||
except Exception,e:
|
|
||||||
_logger.debug("Fail auth: %s" % e )
|
|
||||||
return False
|
|
||||||
|
|
||||||
def checkRequest(self,handler,path, db=False):
|
|
||||||
auth_str = handler.headers.get('Authorization',False)
|
|
||||||
try:
|
|
||||||
if not db:
|
|
||||||
db = handler.get_db_from_path(path)
|
|
||||||
except Exception:
|
|
||||||
if path.startswith('/'):
|
|
||||||
path = path[1:]
|
|
||||||
psp= path.split('/')
|
|
||||||
if len(psp)>1:
|
|
||||||
db = psp[0]
|
|
||||||
else:
|
|
||||||
#FIXME!
|
|
||||||
_logger.info("Wrong path: %s, failing auth" %path)
|
|
||||||
raise AuthRejectedExc("Authorization failed. Wrong sub-path.")
|
|
||||||
if self.auth_creds.get(db):
|
|
||||||
return True
|
|
||||||
if auth_str and auth_str.startswith('Basic '):
|
|
||||||
auth_str=auth_str[len('Basic '):]
|
|
||||||
(user,passwd) = base64.decodestring(auth_str).split(':')
|
|
||||||
_logger.info("Found user=\"%s\", passwd=\"***\" for db=\"%s\"", user, db)
|
|
||||||
acd = self.authenticate(db,user,passwd,handler.client_address)
|
|
||||||
if acd != False:
|
|
||||||
self.auth_creds[db] = acd
|
|
||||||
self.last_auth = db
|
|
||||||
return True
|
|
||||||
if self.auth_tries > 5:
|
|
||||||
_logger.info("Failing authorization after 5 requests w/o password")
|
|
||||||
raise AuthRejectedExc("Authorization failed.")
|
|
||||||
self.auth_tries += 1
|
|
||||||
raise AuthRequiredExc(atype='Basic', realm=self.realm)
|
|
||||||
|
|
||||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
|
@ -164,153 +164,6 @@ def wsgi_xmlrpc(environ, start_response):
|
||||||
params, method = xmlrpclib.loads(data)
|
params, method = xmlrpclib.loads(data)
|
||||||
return xmlrpc_return(start_response, service, method, params, string_faultcode)
|
return xmlrpc_return(start_response, service, method, params, string_faultcode)
|
||||||
|
|
||||||
def wsgi_webdav(environ, start_response):
|
|
||||||
pi = environ['PATH_INFO']
|
|
||||||
if environ['REQUEST_METHOD'] == 'OPTIONS' and pi in ['*','/']:
|
|
||||||
return return_options(environ, start_response)
|
|
||||||
elif pi.startswith('/webdav'):
|
|
||||||
http_dir = websrv_lib.find_http_service(pi)
|
|
||||||
if http_dir:
|
|
||||||
path = pi[len(http_dir.path):]
|
|
||||||
if path.startswith('/'):
|
|
||||||
environ['PATH_INFO'] = path
|
|
||||||
else:
|
|
||||||
environ['PATH_INFO'] = '/' + path
|
|
||||||
return http_to_wsgi(http_dir)(environ, start_response)
|
|
||||||
|
|
||||||
def return_options(environ, start_response):
|
|
||||||
# Microsoft specific header, see
|
|
||||||
# http://www.ibm.com/developerworks/rational/library/2089.html
|
|
||||||
if 'Microsoft' in environ.get('User-Agent', ''):
|
|
||||||
options = [('MS-Author-Via', 'DAV')]
|
|
||||||
else:
|
|
||||||
options = []
|
|
||||||
options += [('DAV', '1 2'), ('Allow', 'GET HEAD PROPFIND OPTIONS REPORT')]
|
|
||||||
start_response("200 OK", [('Content-Length', str(0))] + options)
|
|
||||||
return []
|
|
||||||
|
|
||||||
def http_to_wsgi(http_dir):
|
|
||||||
"""
|
|
||||||
Turn a BaseHTTPRequestHandler into a WSGI entry point.
|
|
||||||
|
|
||||||
Actually the argument is not a bare BaseHTTPRequestHandler but is wrapped
|
|
||||||
(as a class, so it needs to be instanciated) in a HTTPDir.
|
|
||||||
|
|
||||||
This code is adapted from wbsrv_lib.MultiHTTPHandler._handle_one_foreign().
|
|
||||||
It is a temporary solution: the HTTP sub-handlers (in particular the
|
|
||||||
document_webdav addon) have to be WSGIfied.
|
|
||||||
"""
|
|
||||||
def wsgi_handler(environ, start_response):
|
|
||||||
|
|
||||||
headers = {}
|
|
||||||
for key, value in environ.items():
|
|
||||||
if key.startswith('HTTP_'):
|
|
||||||
key = key[5:].replace('_', '-').title()
|
|
||||||
headers[key] = value
|
|
||||||
if key == 'CONTENT_LENGTH':
|
|
||||||
key = key.replace('_', '-').title()
|
|
||||||
headers[key] = value
|
|
||||||
if environ.get('Content-Type'):
|
|
||||||
headers['Content-Type'] = environ['Content-Type']
|
|
||||||
|
|
||||||
path = urllib.quote(environ.get('PATH_INFO', ''))
|
|
||||||
if environ.get('QUERY_STRING'):
|
|
||||||
path += '?' + environ['QUERY_STRING']
|
|
||||||
|
|
||||||
request_version = 'HTTP/1.1' # TODO
|
|
||||||
request_line = "%s %s %s\n" % (environ['REQUEST_METHOD'], path, request_version)
|
|
||||||
|
|
||||||
class Dummy(object):
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Let's pretend we have a server to hand to the handler.
|
|
||||||
server = Dummy()
|
|
||||||
server.server_name = environ['SERVER_NAME']
|
|
||||||
server.server_port = int(environ['SERVER_PORT'])
|
|
||||||
|
|
||||||
# Initialize the underlying handler and associated auth. provider.
|
|
||||||
con = openerp.service.websrv_lib.noconnection(environ['wsgi.input'])
|
|
||||||
handler = http_dir.instanciate_handler(con, environ['REMOTE_ADDR'], server)
|
|
||||||
|
|
||||||
# Populate the handler as if it is called by a regular HTTP server
|
|
||||||
# and the request is already parsed.
|
|
||||||
handler.wfile = StringIO.StringIO()
|
|
||||||
handler.rfile = environ['wsgi.input']
|
|
||||||
handler.headers = headers
|
|
||||||
handler.command = environ['REQUEST_METHOD']
|
|
||||||
handler.path = path
|
|
||||||
handler.request_version = request_version
|
|
||||||
handler.close_connection = 1
|
|
||||||
handler.raw_requestline = request_line
|
|
||||||
handler.requestline = request_line
|
|
||||||
|
|
||||||
# Handle authentication if there is an auth. provider associated to
|
|
||||||
# the handler.
|
|
||||||
if hasattr(handler, 'auth_provider'):
|
|
||||||
try:
|
|
||||||
handler.auth_provider.checkRequest(handler, path)
|
|
||||||
except websrv_lib.AuthRequiredExc, ae:
|
|
||||||
# Darwin 9.x.x webdav clients will report "HTTP/1.0" to us, while they support (and need) the
|
|
||||||
# authorisation features of HTTP/1.1
|
|
||||||
if request_version != 'HTTP/1.1' and ('Darwin/9.' not in handler.headers.get('User-Agent', '')):
|
|
||||||
start_response("403 Forbidden", [])
|
|
||||||
return []
|
|
||||||
start_response("401 Authorization required", [
|
|
||||||
('WWW-Authenticate', '%s realm="%s"' % (ae.atype,ae.realm)),
|
|
||||||
# ('Connection', 'keep-alive'),
|
|
||||||
('Content-Type', 'text/html'),
|
|
||||||
('Content-Length', 4), # len(self.auth_required_msg)
|
|
||||||
])
|
|
||||||
return ['Blah'] # self.auth_required_msg
|
|
||||||
except websrv_lib.AuthRejectedExc,e:
|
|
||||||
start_response("403 %s" % (e.args[0],), [])
|
|
||||||
return []
|
|
||||||
|
|
||||||
method_name = 'do_' + handler.command
|
|
||||||
|
|
||||||
# Support the OPTIONS method even when not provided directly by the
|
|
||||||
# handler. TODO I would prefer to remove it and fix the handler if
|
|
||||||
# needed.
|
|
||||||
if not hasattr(handler, method_name):
|
|
||||||
if handler.command == 'OPTIONS':
|
|
||||||
return return_options(environ, start_response)
|
|
||||||
start_response("501 Unsupported method (%r)" % handler.command, [])
|
|
||||||
return []
|
|
||||||
|
|
||||||
# Finally, call the handler's method.
|
|
||||||
try:
|
|
||||||
method = getattr(handler, method_name)
|
|
||||||
method()
|
|
||||||
# The DAV handler buffers its output and provides a _flush()
|
|
||||||
# method.
|
|
||||||
getattr(handler, '_flush', lambda: None)()
|
|
||||||
response = parse_http_response(handler.wfile.getvalue())
|
|
||||||
response_headers = response.getheaders()
|
|
||||||
body = response.read()
|
|
||||||
start_response(str(response.status) + ' ' + response.reason, response_headers)
|
|
||||||
return [body]
|
|
||||||
except (websrv_lib.AuthRejectedExc, websrv_lib.AuthRequiredExc):
|
|
||||||
raise
|
|
||||||
except Exception, e:
|
|
||||||
start_response("500 Internal error", [])
|
|
||||||
return []
|
|
||||||
|
|
||||||
return wsgi_handler
|
|
||||||
|
|
||||||
def parse_http_response(s):
|
|
||||||
""" Turn a HTTP response string into a httplib.HTTPResponse object."""
|
|
||||||
class DummySocket(StringIO.StringIO):
|
|
||||||
"""
|
|
||||||
This is used to provide a StringIO to httplib.HTTPResponse
|
|
||||||
which, instead of taking a file object, expects a socket and
|
|
||||||
uses its makefile() method.
|
|
||||||
"""
|
|
||||||
def makefile(self, *args, **kw):
|
|
||||||
return self
|
|
||||||
response = httplib.HTTPResponse(DummySocket(s))
|
|
||||||
response.begin()
|
|
||||||
return response
|
|
||||||
|
|
||||||
# WSGI handlers registered through the register_wsgi_handler() function below.
|
# WSGI handlers registered through the register_wsgi_handler() function below.
|
||||||
module_handlers = []
|
module_handlers = []
|
||||||
# RPC endpoints registered through the register_rpc_endpoint() function below.
|
# RPC endpoints registered through the register_rpc_endpoint() function below.
|
||||||
|
@ -342,7 +195,7 @@ def application_unproxied(environ, start_response):
|
||||||
del threading.current_thread().dbname
|
del threading.current_thread().dbname
|
||||||
|
|
||||||
# Try all handlers until one returns some result (i.e. not None).
|
# Try all handlers until one returns some result (i.e. not None).
|
||||||
wsgi_handlers = [wsgi_xmlrpc, wsgi_webdav]
|
wsgi_handlers = [wsgi_xmlrpc]
|
||||||
wsgi_handlers += module_handlers
|
wsgi_handlers += module_handlers
|
||||||
for handler in wsgi_handlers:
|
for handler in wsgi_handlers:
|
||||||
result = handler(environ, start_response)
|
result = handler(environ, start_response)
|
||||||
|
|
|
@ -6,7 +6,8 @@ function waitFor (ready, callback, timeout, timeoutMessageCallback) {
|
||||||
|
|
||||||
(function waitLoop() {
|
(function waitLoop() {
|
||||||
if(new Date - start > timeout) {
|
if(new Date - start > timeout) {
|
||||||
error(timeoutMessageCallback ? timeoutMessageCallback() : "Timeout after "+timeout+" ms");
|
console.log('error', timeoutMessageCallback ? timeoutMessageCallback() : "Timeout after "+timeout+" ms");
|
||||||
|
phantom.exit(1);
|
||||||
} else if (ready()) {
|
} else if (ready()) {
|
||||||
callback();
|
callback();
|
||||||
} else {
|
} else {
|
||||||
|
@ -15,10 +16,6 @@ function waitFor (ready, callback, timeout, timeoutMessageCallback) {
|
||||||
}());
|
}());
|
||||||
}
|
}
|
||||||
|
|
||||||
function error(message) {
|
|
||||||
console.log('error', message);
|
|
||||||
phantom.exit(1);
|
|
||||||
}
|
|
||||||
function PhantomTest() {
|
function PhantomTest() {
|
||||||
var self = this;
|
var self = this;
|
||||||
this.options = JSON.parse(phantom.args[phantom.args.length-1]);
|
this.options = JSON.parse(phantom.args[phantom.args.length-1]);
|
||||||
|
@ -49,10 +46,12 @@ function PhantomTest() {
|
||||||
}));
|
}));
|
||||||
msg.push('(leaf frame on top)')
|
msg.push('(leaf frame on top)')
|
||||||
}
|
}
|
||||||
error(JSON.stringify(msg.join('\n')));
|
console.log('error', JSON.stringify(msg.join('\n')));
|
||||||
|
phantom.exit(1);
|
||||||
};
|
};
|
||||||
this.page.onAlert = function(message) {
|
this.page.onAlert = function(message) {
|
||||||
error(message);
|
console.log('error', message);
|
||||||
|
phantom.exit(1);
|
||||||
};
|
};
|
||||||
this.page.onConsoleMessage = function(message) {
|
this.page.onConsoleMessage = function(message) {
|
||||||
console.log(message);
|
console.log(message);
|
||||||
|
@ -78,7 +77,8 @@ function PhantomTest() {
|
||||||
if(!found) {
|
if(!found) {
|
||||||
console.log('Injecting', src, 'needed for', need);
|
console.log('Injecting', src, 'needed for', need);
|
||||||
if(!self.page.injectJs(src)) {
|
if(!self.page.injectJs(src)) {
|
||||||
error("Cannot inject " + src);
|
console.log('error', "Cannot inject " + src);
|
||||||
|
phantom.exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,8 +88,9 @@ function PhantomTest() {
|
||||||
self.page.evaluate(function () {
|
self.page.evaluate(function () {
|
||||||
var message = ("Timeout\nhref: " + window.location.href
|
var message = ("Timeout\nhref: " + window.location.href
|
||||||
+ "\nreferrer: " + document.referrer
|
+ "\nreferrer: " + document.referrer
|
||||||
+ "\n\n" + document.body.innerHTML).replace(/[^a-z0-9\s~!@#$%^&*()_|+\-=?;:'",.<>\{\}\[\]\\\/]/gi, "*");
|
+ "\n\n" + (document.body && document.body.innerHTML)).replace(/[^a-z0-9\s~!@#$%^&*()_|+\-=?;:'",.<>\{\}\[\]\\\/]/gi, "*");
|
||||||
error(message);
|
console.log('error', message);
|
||||||
|
phantom.exit(1);
|
||||||
});
|
});
|
||||||
}, self.timeout);
|
}, self.timeout);
|
||||||
|
|
||||||
|
@ -108,7 +109,8 @@ function PhantomTest() {
|
||||||
var url = self.origin + url_path;
|
var url = self.origin + url_path;
|
||||||
self.page.open(url, function(status) {
|
self.page.open(url, function(status) {
|
||||||
if (status !== 'success') {
|
if (status !== 'success') {
|
||||||
error("failed to load " + url)
|
console.log('error', "failed to load " + url);
|
||||||
|
phantom.exit(1);
|
||||||
} else {
|
} else {
|
||||||
console.log('loaded', url, status);
|
console.log('loaded', url, status);
|
||||||
// process ready
|
// process ready
|
||||||
|
@ -119,7 +121,7 @@ function PhantomTest() {
|
||||||
try {
|
try {
|
||||||
console.log("page.evaluate eval expr:", ready);
|
console.log("page.evaluate eval expr:", ready);
|
||||||
r = !!eval(ready);
|
r = !!eval(ready);
|
||||||
} catch(ex) {
|
} catch(ex) {
|
||||||
}
|
}
|
||||||
console.log("page.evaluate eval result:", r);
|
console.log("page.evaluate eval result:", r);
|
||||||
return r;
|
return r;
|
||||||
|
|
|
@ -152,19 +152,11 @@ class configmanager(object):
|
||||||
parser.add_option_group(group)
|
parser.add_option_group(group)
|
||||||
|
|
||||||
# WEB
|
# WEB
|
||||||
# TODO move to web addons after MetaOption merge
|
|
||||||
group = optparse.OptionGroup(parser, "Web interface Configuration")
|
group = optparse.OptionGroup(parser, "Web interface Configuration")
|
||||||
group.add_option("--db-filter", dest="dbfilter", default='.*',
|
group.add_option("--db-filter", dest="dbfilter", default='.*',
|
||||||
help="Filter listed database", metavar="REGEXP")
|
help="Filter listed database", metavar="REGEXP")
|
||||||
parser.add_option_group(group)
|
parser.add_option_group(group)
|
||||||
|
|
||||||
# Static HTTP
|
|
||||||
group = optparse.OptionGroup(parser, "Static HTTP service")
|
|
||||||
group.add_option("--static-http-enable", dest="static_http_enable", action="store_true", my_default=False, help="enable static HTTP service for serving plain HTML files")
|
|
||||||
group.add_option("--static-http-document-root", dest="static_http_document_root", help="specify the directory containing your static HTML files (e.g '/var/www/')")
|
|
||||||
group.add_option("--static-http-url-prefix", dest="static_http_url_prefix", help="specify the URL root prefix where you want web browsers to access your static HTML files (e.g '/')")
|
|
||||||
parser.add_option_group(group)
|
|
||||||
|
|
||||||
# Testing Group
|
# Testing Group
|
||||||
group = optparse.OptionGroup(parser, "Testing Configuration")
|
group = optparse.OptionGroup(parser, "Testing Configuration")
|
||||||
group.add_option("--test-file", dest="test_file", my_default=False,
|
group.add_option("--test-file", dest="test_file", my_default=False,
|
||||||
|
@ -313,9 +305,10 @@ class configmanager(object):
|
||||||
self.options[option.dest] = option.my_default
|
self.options[option.dest] = option.my_default
|
||||||
self.casts[option.dest] = option
|
self.casts[option.dest] = option
|
||||||
|
|
||||||
self.parse_config(None, False)
|
# generate default config
|
||||||
|
self._parse_config()
|
||||||
|
|
||||||
def parse_config(self, args=None, complete=True):
|
def parse_config(self, args=None):
|
||||||
""" Parse the configuration file (if any) and the command-line
|
""" Parse the configuration file (if any) and the command-line
|
||||||
arguments.
|
arguments.
|
||||||
|
|
||||||
|
@ -329,10 +322,12 @@ class configmanager(object):
|
||||||
Typical usage of this method:
|
Typical usage of this method:
|
||||||
|
|
||||||
openerp.tools.config.parse_config(sys.argv[1:])
|
openerp.tools.config.parse_config(sys.argv[1:])
|
||||||
|
|
||||||
:param complete: this is a hack used in __init__(), leave it to True.
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
self._parse_config(args)
|
||||||
|
openerp.netsvc.init_logger()
|
||||||
|
openerp.modules.module.initialize_sys_path()
|
||||||
|
|
||||||
|
def _parse_config(self, args=None):
|
||||||
if args is None:
|
if args is None:
|
||||||
args = []
|
args = []
|
||||||
opt, args = self.parser.parse_args(args)
|
opt, args = self.parser.parse_args(args)
|
||||||
|
@ -391,7 +386,6 @@ class configmanager(object):
|
||||||
'db_maxconn', 'import_partial', 'addons_path',
|
'db_maxconn', 'import_partial', 'addons_path',
|
||||||
'xmlrpc', 'syslog', 'without_demo', 'timezone',
|
'xmlrpc', 'syslog', 'without_demo', 'timezone',
|
||||||
'xmlrpcs_interface', 'xmlrpcs_port', 'xmlrpcs',
|
'xmlrpcs_interface', 'xmlrpcs_port', 'xmlrpcs',
|
||||||
'static_http_enable', 'static_http_document_root', 'static_http_url_prefix',
|
|
||||||
'secure_cert_file', 'secure_pkey_file', 'dbfilter', 'log_handler', 'log_level', 'log_db'
|
'secure_cert_file', 'secure_pkey_file', 'dbfilter', 'log_handler', 'log_level', 'log_db'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -503,8 +497,6 @@ class configmanager(object):
|
||||||
openerp.conf.server_wide_modules = map(lambda m: m.strip(), opt.server_wide_modules.split(','))
|
openerp.conf.server_wide_modules = map(lambda m: m.strip(), opt.server_wide_modules.split(','))
|
||||||
else:
|
else:
|
||||||
openerp.conf.server_wide_modules = ['web','web_kanban']
|
openerp.conf.server_wide_modules = ['web','web_kanban']
|
||||||
if complete:
|
|
||||||
openerp.modules.module.initialize_sys_path()
|
|
||||||
|
|
||||||
def _generate_pgpassfile(self):
|
def _generate_pgpassfile(self):
|
||||||
"""
|
"""
|
||||||
|
@ -662,6 +654,9 @@ class configmanager(object):
|
||||||
os.chmod(d, 0700)
|
os.chmod(d, 0700)
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
def filestore(self, dbname):
|
||||||
|
return os.path.join(self['data_dir'], 'filestore', dbname)
|
||||||
|
|
||||||
config = configmanager()
|
config = configmanager()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
#
|
#
|
||||||
# OpenERP, Open Source Management Solution
|
# OpenERP, Open Source Management Solution
|
||||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||||
# Copyright (C) 2010 OpenERP s.a. (<http://openerp.com>).
|
# Copyright (C) 2010, 2014 OpenERP s.a. (<http://openerp.com>).
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU Affero General Public License as
|
# it under the terms of the GNU Affero General Public License as
|
||||||
|
@ -20,11 +20,39 @@
|
||||||
#
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
__all__ = ['synchronized']
|
__all__ = ['synchronized', 'lazy_property']
|
||||||
|
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from inspect import getsourcefile
|
from inspect import getsourcefile
|
||||||
|
|
||||||
|
|
||||||
|
class lazy_property(object):
|
||||||
|
""" Decorator for a lazy property of an object, i.e., an object attribute
|
||||||
|
that is determined by the result of a method call evaluated once. To
|
||||||
|
reevaluate the property, simply delete the attribute on the object, and
|
||||||
|
get it again.
|
||||||
|
"""
|
||||||
|
def __init__(self, fget):
|
||||||
|
self.fget = fget
|
||||||
|
self.name = fget.__name__
|
||||||
|
|
||||||
|
def __get__(self, obj, cls):
|
||||||
|
if obj is None:
|
||||||
|
return self
|
||||||
|
value = self.fget(obj)
|
||||||
|
setattr(obj, self.name, value)
|
||||||
|
return value
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def reset_all(obj):
|
||||||
|
""" Reset all lazy properties on the instance `obj`. """
|
||||||
|
cls = type(obj)
|
||||||
|
obj_dict = obj.__dict__
|
||||||
|
for name in obj_dict.keys():
|
||||||
|
if isinstance(getattr(cls, name, None), lazy_property):
|
||||||
|
obj_dict.pop(name)
|
||||||
|
|
||||||
|
|
||||||
def synchronized(lock_attr='_lock'):
|
def synchronized(lock_attr='_lock'):
|
||||||
def decorator(func):
|
def decorator(func):
|
||||||
@wraps(func)
|
@wraps(func)
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
#
|
#
|
||||||
# OpenERP, Open Source Management Solution
|
# OpenERP, Open Source Management Solution
|
||||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||||
# Copyright (C) 2010-2013 OpenERP s.a. (<http://openerp.com>).
|
# Copyright (C) 2010-2014 OpenERP s.a. (<http://openerp.com>).
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU Affero General Public License as
|
# it under the terms of the GNU Affero General Public License as
|
||||||
|
@ -83,7 +83,8 @@ def exec_pg_command(name, *args):
|
||||||
raise Exception('Couldn\'t find %s' % name)
|
raise Exception('Couldn\'t find %s' % name)
|
||||||
args2 = (prog,) + args
|
args2 = (prog,) + args
|
||||||
|
|
||||||
return subprocess.call(args2)
|
with open(os.devnull) as dn:
|
||||||
|
return subprocess.call(args2, stdout=dn, stderr=subprocess.STDOUT)
|
||||||
|
|
||||||
def exec_pg_command_pipe(name, *args):
|
def exec_pg_command_pipe(name, *args):
|
||||||
prog = find_pg_tool(name)
|
prog = find_pg_tool(name)
|
||||||
|
|
Loading…
Reference in New Issue