[MERGE] forward merge 7.0 until revision 4919.
bzr revid: vmt@openerp.com-20130404130704-24vsmczw34cssytd
This commit is contained in:
commit
307ca374d6
|
@ -19,6 +19,7 @@ Depends:
|
|||
python-docutils,
|
||||
python-feedparser,
|
||||
python-gdata,
|
||||
python-imaging,
|
||||
python-jinja2,
|
||||
python-ldap,
|
||||
python-libxslt1,
|
||||
|
@ -46,7 +47,7 @@ Depends:
|
|||
Conflicts: tinyerp-server, openerp-server, openerp-web
|
||||
Replaces: tinyerp-server, openerp-server, openerp-web
|
||||
Recommends:
|
||||
graphviz, ghostscript, postgresql, python-imaging, python-matplotlib
|
||||
graphviz, ghostscript, postgresql, python-matplotlib, poppler-utils
|
||||
Description: OpenERP Enterprise Resource Management
|
||||
OpenERP, previously known as TinyERP, is a complete ERP and CRM. The main
|
||||
features are accounting (analytic and financial), stock management, sales and
|
||||
|
|
|
@ -17,55 +17,46 @@ DAEMON=/usr/bin/openerp-server
|
|||
NAME=openerp-server
|
||||
DESC=openerp-server
|
||||
CONFIG=/etc/openerp/openerp-server.conf
|
||||
LOGFILE=/var/log/openerp-server.log
|
||||
LOGFILE=/var/log/openerp/openerp-server.log
|
||||
USER=openerp
|
||||
|
||||
test -x ${DAEMON} || exit 0
|
||||
|
||||
set -e
|
||||
|
||||
do_start () {
|
||||
echo -n "Starting ${DESC}: "
|
||||
start-stop-daemon --start --quiet --pidfile /var/run/${NAME}.pid --chuid ${USER} --background --make-pidfile --exec ${DAEMON} -- --config=${CONFIG} --logfile=${LOGFILE}
|
||||
echo "${NAME}."
|
||||
}
|
||||
|
||||
do_stop () {
|
||||
echo -n "Stopping ${DESC}: "
|
||||
start-stop-daemon --stop --quiet --pidfile /var/run/${NAME}.pid --oknodo
|
||||
echo "${NAME}."
|
||||
}
|
||||
|
||||
case "${1}" in
|
||||
start)
|
||||
echo -n "Starting ${DESC}: "
|
||||
start)
|
||||
do_start
|
||||
;;
|
||||
|
||||
start-stop-daemon --start --quiet --pidfile /var/run/${NAME}.pid \
|
||||
--chuid ${USER} --background --make-pidfile \
|
||||
--exec ${DAEMON} -- --config=${CONFIG} \
|
||||
--logfile=${LOGFILE}
|
||||
stop)
|
||||
do_stop
|
||||
;;
|
||||
|
||||
echo "${NAME}."
|
||||
;;
|
||||
restart|force-reload)
|
||||
echo -n "Restarting ${DESC}: "
|
||||
do_stop
|
||||
sleep 1
|
||||
do_start
|
||||
;;
|
||||
|
||||
stop)
|
||||
echo -n "Stopping ${DESC}: "
|
||||
|
||||
start-stop-daemon --stop --quiet --pidfile /var/run/${NAME}.pid \
|
||||
--oknodo
|
||||
|
||||
echo "${NAME}."
|
||||
;;
|
||||
|
||||
restart|force-reload)
|
||||
echo -n "Restarting ${DESC}: "
|
||||
|
||||
start-stop-daemon --stop --quiet --pidfile /var/run/${NAME}.pid \
|
||||
--oknodo
|
||||
|
||||
sleep 1
|
||||
|
||||
start-stop-daemon --start --quiet --pidfile /var/run/${NAME}.pid \
|
||||
--chuid ${USER} --background --make-pidfile \
|
||||
--exec ${DAEMON} -- --config=${CONFIG} \
|
||||
--logfile=${LOGFILE}
|
||||
|
||||
echo "${NAME}."
|
||||
;;
|
||||
|
||||
*)
|
||||
N=/etc/init.d/${NAME}
|
||||
echo "Usage: ${NAME} {start|stop|restart|force-reload}" >&2
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
N=/etc/init.d/${NAME}
|
||||
echo "Usage: ${NAME} {start|stop|restart|force-reload}" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
|
|
|
@ -12,9 +12,9 @@ case "${1}" in
|
|||
chown openerp:openerp /etc/openerp/openerp-server.conf
|
||||
chmod 0640 /etc/openerp/openerp-server.conf
|
||||
# Creating log file
|
||||
touch /var/log/openerp-server.log
|
||||
chown openerp:openerp /var/log/openerp-server.log
|
||||
chmod 0640 /var/log/openerp-server.log
|
||||
mkdir -p /var/log/openerp/
|
||||
chown openerp:openerp /var/log/openerp
|
||||
chmod 0750 /var/log/openerp
|
||||
# Creating local storage directory
|
||||
mkdir -p /var/lib/openerp/filestore
|
||||
chown openerp:openerp -R /var/lib/openerp
|
||||
|
|
|
@ -22,6 +22,17 @@
|
|||
""" OpenERP core library.
|
||||
|
||||
"""
|
||||
|
||||
# Make sure the OpenERP server runs in UTC. This is especially necessary
|
||||
# under Windows as under Linux it seems the real import of time is
|
||||
# sufficiently deferred so that setting the TZ environment variable
|
||||
# in openerp.cli.server was working.
|
||||
import os
|
||||
os.environ['TZ'] = 'UTC' # Set the timezone...
|
||||
import time # ... *then* import time.
|
||||
del os
|
||||
del time
|
||||
|
||||
# The hard-coded super-user id (a.k.a. administrator, or root user).
|
||||
SUPERUSER_ID = 1
|
||||
|
||||
|
|
|
@ -632,7 +632,7 @@ class actions_server(osv.osv):
|
|||
.read(cr, uid, action.action_id.id, context=context)
|
||||
|
||||
if action.state=='code':
|
||||
eval(action.code, cxt, mode="exec", nocopy=True) # nocopy allows to return 'action'
|
||||
eval(action.code.strip(), cxt, mode="exec", nocopy=True) # nocopy allows to return 'action'
|
||||
if 'action' in cxt:
|
||||
return cxt['action']
|
||||
|
||||
|
|
|
@ -83,7 +83,7 @@ class ir_attachment(osv.osv):
|
|||
if bin_size:
|
||||
r = os.path.getsize(full_path)
|
||||
else:
|
||||
r = open(full_path).read().encode('base64')
|
||||
r = open(full_path,'rb').read().encode('base64')
|
||||
except IOError:
|
||||
_logger.error("_read_file reading %s",full_path)
|
||||
return r
|
||||
|
|
|
@ -18,8 +18,9 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
import time
|
||||
import logging
|
||||
import threading
|
||||
import time
|
||||
import psycopg2
|
||||
from datetime import datetime
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
@ -188,6 +189,7 @@ class ir_cron(osv.osv):
|
|||
If a job was processed, returns True, otherwise returns False.
|
||||
"""
|
||||
db = openerp.sql_db.db_connect(db_name)
|
||||
threading.current_thread().dbname = db_name
|
||||
cr = db.cursor()
|
||||
jobs = []
|
||||
try:
|
||||
|
@ -242,6 +244,9 @@ class ir_cron(osv.osv):
|
|||
# we're exiting due to an exception while acquiring the lock
|
||||
lock_cr.close()
|
||||
|
||||
if hasattr(threading.current_thread(), 'dbname'): # cron job could have removed it as side-effect
|
||||
del threading.current_thread().dbname
|
||||
|
||||
def _try_lock(self, cr, uid, ids, context=None):
|
||||
"""Try to grab a dummy exclusive write-lock to the rows with the given ids,
|
||||
to make sure a following write() or unlink() will not block due
|
||||
|
|
|
@ -28,7 +28,7 @@ class ir_filters(osv.osv):
|
|||
_description = 'Filters'
|
||||
|
||||
def _list_all_models(self, cr, uid, context=None):
|
||||
cr.execute("SELECT model, name from ir_model")
|
||||
cr.execute("SELECT model, name FROM ir_model ORDER BY name")
|
||||
return cr.fetchall()
|
||||
|
||||
def copy(self, cr, uid, id, default=None, context=None):
|
||||
|
|
|
@ -25,6 +25,7 @@ import time
|
|||
import types
|
||||
|
||||
import openerp
|
||||
import openerp.modules.registry
|
||||
from openerp import SUPERUSER_ID
|
||||
from openerp import tools
|
||||
from openerp.osv import fields,osv
|
||||
|
@ -168,7 +169,9 @@ class ir_model(osv.osv):
|
|||
if not context.get(MODULE_UNINSTALL_FLAG):
|
||||
# only reload pool for normal unlink. For module uninstall the
|
||||
# reload is done independently in openerp.modules.loading
|
||||
cr.commit() # must be committed before reloading registry in new cursor
|
||||
openerp.modules.registry.RegistryManager.new(cr.dbname)
|
||||
openerp.modules.registry.RegistryManager.signal_registry_change(cr.dbname)
|
||||
|
||||
return res
|
||||
|
||||
|
@ -194,6 +197,7 @@ class ir_model(osv.osv):
|
|||
field_state='manual',
|
||||
select=vals.get('select_level', '0'))
|
||||
self.pool[vals['model']]._auto_init(cr, ctx)
|
||||
openerp.modules.registry.RegistryManager.signal_registry_change(cr.dbname)
|
||||
return res
|
||||
|
||||
def instanciate(self, cr, user, model, context=None):
|
||||
|
@ -259,7 +263,6 @@ class ir_model_fields(osv.osv):
|
|||
'state': lambda self,cr,uid,ctx=None: (ctx and ctx.get('manual',False)) and 'manual' or 'base',
|
||||
'on_delete': 'set null',
|
||||
'select_level': '0',
|
||||
'size': 64,
|
||||
'field_description': '',
|
||||
'selectable': 1,
|
||||
}
|
||||
|
@ -289,10 +292,10 @@ class ir_model_fields(osv.osv):
|
|||
return True
|
||||
|
||||
def _size_gt_zero_msg(self, cr, user, ids, context=None):
|
||||
return _('Size of the field can never be less than 1 !')
|
||||
return _('Size of the field can never be less than 0 !')
|
||||
|
||||
_sql_constraints = [
|
||||
('size_gt_zero', 'CHECK (size>0)',_size_gt_zero_msg ),
|
||||
('size_gt_zero', 'CHECK (size>=0)',_size_gt_zero_msg ),
|
||||
]
|
||||
|
||||
def _drop_column(self, cr, uid, ids, context=None):
|
||||
|
@ -318,6 +321,9 @@ class ir_model_fields(osv.osv):
|
|||
|
||||
self._drop_column(cr, user, ids, context)
|
||||
res = super(ir_model_fields, self).unlink(cr, user, ids, context)
|
||||
if not context.get(MODULE_UNINSTALL_FLAG):
|
||||
cr.commit()
|
||||
openerp.modules.registry.RegistryManager.signal_registry_change(cr.dbname)
|
||||
return res
|
||||
|
||||
def create(self, cr, user, vals, context=None):
|
||||
|
@ -349,6 +355,7 @@ class ir_model_fields(osv.osv):
|
|||
select=vals.get('select_level', '0'),
|
||||
update_custom_fields=True)
|
||||
self.pool[vals['model']]._auto_init(cr, ctx)
|
||||
openerp.modules.registry.RegistryManager.signal_registry_change(cr.dbname)
|
||||
|
||||
return res
|
||||
|
||||
|
@ -465,6 +472,7 @@ class ir_model_fields(osv.osv):
|
|||
for col_name, col_prop, val in patch_struct[1]:
|
||||
setattr(obj._columns[col_name], col_prop, val)
|
||||
obj._auto_init(cr, ctx)
|
||||
openerp.modules.registry.RegistryManager.signal_registry_change(cr.dbname)
|
||||
return res
|
||||
|
||||
class ir_model_constraint(Model):
|
||||
|
|
|
@ -151,7 +151,7 @@
|
|||
'readonly': [('ttype','not in', ['many2one','one2many','many2many'])]}"/>
|
||||
<field name="relation_field" attrs="{'required': [('ttype','=','one2many')], 'readonly': [('ttype','!=','one2many')]}"/>
|
||||
<field name="selection" attrs="{'required': [('ttype','in',['selection','reference'])], 'readonly': [('ttype','not in',['selection','reference'])]}"/>
|
||||
<field name="size" attrs="{'required': [('ttype','in',['char','reference'])], 'readonly': [('ttype','not in',['char','reference'])]}"/>
|
||||
<field name="size" attrs="{'invisible': [('ttype','not in',['char','text','reference'])]}"/>
|
||||
<field name="domain" attrs="{'readonly': [('relation','=','')]}"/>
|
||||
<field name="serialization_field_id" attrs="{'readonly': [('state','=','base')]}" domain="[('ttype','=','serialized'), ('model_id', '=', model_id)]"/>
|
||||
<field name="on_delete" attrs="{'readonly': [('ttype','!=','many2one')]}"/>
|
||||
|
|
|
@ -83,7 +83,8 @@ class view(osv.osv):
|
|||
}
|
||||
_defaults = {
|
||||
'arch': '<?xml version="1.0"?>\n<tree string="My view">\n\t<field name="name"/>\n</tree>',
|
||||
'priority': 16
|
||||
'priority': 16,
|
||||
'type': 'tree',
|
||||
}
|
||||
_order = "priority,name"
|
||||
|
||||
|
|
|
@ -411,7 +411,6 @@ class module(osv.osv):
|
|||
if to_install_ids:
|
||||
self.button_install(cr, uid, to_install_ids, context=context)
|
||||
|
||||
openerp.modules.registry.RegistryManager.signal_registry_change(cr.dbname)
|
||||
return dict(ACTION_DICT, name=_('Install'))
|
||||
|
||||
def button_immediate_install(self, cr, uid, ids, context=None):
|
||||
|
@ -500,7 +499,6 @@ class module(osv.osv):
|
|||
raise orm.except_orm(_('Error'), _("The `base` module cannot be uninstalled"))
|
||||
dep_ids = self.downstream_dependencies(cr, uid, ids, context=context)
|
||||
self.write(cr, uid, ids + dep_ids, {'state': 'to remove'})
|
||||
openerp.modules.registry.RegistryManager.signal_registry_change(cr.dbname)
|
||||
return dict(ACTION_DICT, name=_('Uninstall'))
|
||||
|
||||
def button_uninstall_cancel(self, cr, uid, ids, context=None):
|
||||
|
|
|
@ -305,7 +305,7 @@ class res_company(osv.osv):
|
|||
<frame id="first" x1="1.3cm" y1="3.0cm" height="%s" width="19.0cm"/>
|
||||
<stylesheet>
|
||||
<paraStyle name="main_footer" fontName="DejaVu Sans" fontSize="8.0" alignment="CENTER"/>
|
||||
<paraStyle name="main_header" fontName="DejaVu Sans" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/>
|
||||
<paraStyle name="main_header" fontName="DejaVu Sans" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/>
|
||||
</stylesheet>
|
||||
<pageGraphics>
|
||||
<!-- You Logo - Change X,Y,Width and Height -->
|
||||
|
@ -344,8 +344,8 @@ class res_company(osv.osv):
|
|||
</pageTemplate>
|
||||
</header>"""
|
||||
|
||||
_header_a4 = _header_main % ('23.0cm', '27.6cm', '27.7cm', '27.7cm', '27.8cm', '27.3cm', '25.3cm', '25.0cm', '25.0cm', '24.6cm', '24.6cm', '24.5cm', '24.5cm')
|
||||
_header_letter = _header_main % ('21.3cm', '25.9cm', '26.0cm', '26.0cm', '26.1cm', '25.6cm', '23.6cm', '23.3cm', '23.3cm', '22.9cm', '22.9cm', '22.8cm', '22.8cm')
|
||||
_header_a4 = _header_main % ('21.7cm', '27.7cm', '27.7cm', '27.7cm', '27.8cm', '27.3cm', '25.3cm', '25.0cm', '25.0cm', '24.6cm', '24.6cm', '24.5cm', '24.5cm')
|
||||
_header_letter = _header_main % ('20cm', '26.0cm', '26.0cm', '26.0cm', '26.1cm', '25.6cm', '23.6cm', '23.3cm', '23.3cm', '22.9cm', '22.9cm', '22.8cm', '22.8cm')
|
||||
|
||||
def onchange_paper_format(self, cr, uid, ids, paper_format, context=None):
|
||||
if paper_format == 'us_letter':
|
||||
|
|
|
@ -49,7 +49,7 @@ class res_currency(osv.osv):
|
|||
id, rate = cr.fetchall()[0]
|
||||
res[id] = rate
|
||||
else:
|
||||
res[id] = 0
|
||||
raise osv.except_osv(_('Error!'),_("No currency rate associated for currency %d for the given period" % (id)))
|
||||
return res
|
||||
_name = "res.currency"
|
||||
_description = "Currency"
|
||||
|
|
|
@ -375,16 +375,30 @@ class res_partner(osv.osv, format_address):
|
|||
|
||||
def create(self, cr, uid, vals, context=None):
|
||||
if context is None:
|
||||
context={}
|
||||
context = {}
|
||||
# Update parent and siblings records
|
||||
if vals.get('parent_id') and vals.get('use_parent_address'):
|
||||
domain_siblings = [('parent_id', '=', vals['parent_id']), ('use_parent_address', '=', True)]
|
||||
update_ids = [vals['parent_id']] + self.search(cr, uid, domain_siblings, context=context)
|
||||
self.update_address(cr, uid, update_ids, vals, context)
|
||||
return super(res_partner,self).create(cr, uid, vals, context=context)
|
||||
if vals.get('parent_id'):
|
||||
if 'use_parent_address' in vals:
|
||||
use_parent_address = vals['use_parent_address']
|
||||
else:
|
||||
use_parent_address = self.default_get(cr, uid, ['use_parent_address'], context=context)['use_parent_address']
|
||||
|
||||
if use_parent_address:
|
||||
domain_siblings = [('parent_id', '=', vals['parent_id']), ('use_parent_address', '=', True)]
|
||||
update_ids = [vals['parent_id']] + self.search(cr, uid, domain_siblings, context=context)
|
||||
self.update_address(cr, uid, update_ids, vals, context)
|
||||
|
||||
# add missing address keys
|
||||
onchange_values = self.onchange_address(cr, uid, [], use_parent_address,
|
||||
vals['parent_id'], context=context).get('value') or {}
|
||||
vals.update(dict((key, value)
|
||||
for key, value in onchange_values.iteritems()
|
||||
if key in ADDRESS_FIELDS and key not in vals))
|
||||
|
||||
return super(res_partner, self).create(cr, uid, vals, context=context)
|
||||
|
||||
def update_address(self, cr, uid, ids, vals, context=None):
|
||||
addr_vals = dict((key, vals[key]) for key in POSTAL_ADDRESS_FIELDS if vals.get(key))
|
||||
addr_vals = dict((key, vals[key]) for key in POSTAL_ADDRESS_FIELDS if key in vals)
|
||||
if addr_vals:
|
||||
return super(res_partner, self).write(cr, uid, ids, addr_vals, context)
|
||||
|
||||
|
@ -411,10 +425,10 @@ class res_partner(osv.osv, format_address):
|
|||
""" Supported syntax:
|
||||
- 'Raoul <raoul@grosbedon.fr>': will find name and email address
|
||||
- otherwise: default, everything is set as the name """
|
||||
match = re.search(r'([^\s,<@]+@[^>\s,]+)', text)
|
||||
if match:
|
||||
email = match.group(1)
|
||||
name = text[:text.index(email)].replace('"','').replace('<','').strip()
|
||||
emails = tools.email_split(text)
|
||||
if emails:
|
||||
email = emails[0]
|
||||
name = text[:text.index(email)].replace('"', '').replace('<', '').strip()
|
||||
else:
|
||||
name, email = text, ''
|
||||
return name, email
|
||||
|
@ -457,8 +471,7 @@ class res_partner(osv.osv, format_address):
|
|||
OR partner.name || ' (' || COALESCE(company.name,'') || ')'
|
||||
''' + operator + ' %(name)s ' + limit_str, query_args)
|
||||
ids = map(lambda x: x[0], cr.fetchall())
|
||||
if args:
|
||||
ids = self.search(cr, uid, [('id', 'in', ids)] + args, limit=limit, context=context)
|
||||
ids = self.search(cr, uid, [('id', 'in', ids)] + args, limit=limit, context=context)
|
||||
if ids:
|
||||
return self.name_get(cr, uid, ids, context)
|
||||
return super(res_partner,self).name_search(cr, uid, name, args, operator=operator, context=context, limit=limit)
|
||||
|
|
|
@ -305,15 +305,15 @@
|
|||
filter_domain="['|','|',('name','ilike',self),('parent_id','ilike',self),('ref','=',self)]"/>
|
||||
<filter help="My Partners" icon="terp-personal+" domain="[('user_id','=',uid)]"/>
|
||||
<separator/>
|
||||
<filter string="Persons" name="type_person" icon="terp-personal" domain="[('is_company','=',0)]"/>
|
||||
<filter string="Companies" name="type_company" icon="terp-partner" domain="[('is_company','=',1)]"/>
|
||||
<filter string="Persons" name="type_person" domain="[('is_company','=',0)]"/>
|
||||
<filter string="Companies" name="type_company" domain="[('is_company','=',1)]"/>
|
||||
<separator/>
|
||||
<filter string="Customers" name="customer" icon="terp-personal" domain="[('customer','=',1)]" help="Customer Partners"/>
|
||||
<filter string="Customers" name="customer" domain="[('customer','=',1)]" help="Customer Partners"/>
|
||||
<filter string="Suppliers" name="supplier" domain="[('supplier','=',1)]" help="Supplier Partners"/>
|
||||
<separator/>
|
||||
<filter string="Suppliers" name="supplier" icon="terp-personal" domain="[('supplier','=',1)]" help="Supplier Partners"/>
|
||||
<field name="category_id" string="Tag" filter_domain="[('category_id','ilike', self)]"/>
|
||||
<field name="user_id"/>
|
||||
<field name="parent_id" filter_domain="[('parent_id','child_of',[self])]"/>
|
||||
<field name="parent_id" domain="[('is_company','=',1)]" filter_domain="[('parent_id','child_of',[self])]"/>
|
||||
<group expand="0" string="Group By...">
|
||||
<filter string="Salesperson" icon="terp-personal" domain="[]" context="{'group_by' : 'user_id'}" />
|
||||
<filter string="Company" context="{'group_by': 'parent_id'}"/>
|
||||
|
|
|
@ -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-2013 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
|
||||
|
@ -172,6 +172,10 @@ class res_users(osv.osv):
|
|||
}
|
||||
}
|
||||
|
||||
def onchange_state(self, cr, uid, ids, state_id, context=None):
|
||||
partner_ids = [user.partner_id.id for user in self.browse(cr, uid, ids, context=context)]
|
||||
return self.pool.get('res.partner').onchange_state(cr, uid, partner_ids, state_id, context=context)
|
||||
|
||||
def onchange_type(self, cr, uid, ids, is_company, context=None):
|
||||
""" Wrapper on the user.partner onchange_type, because some calls to the
|
||||
partner form view applied to the user may trigger the
|
||||
|
@ -426,7 +430,9 @@ class res_users(osv.osv):
|
|||
cr = self.pool.db.cursor()
|
||||
try:
|
||||
base = user_agent_env['base_location']
|
||||
self.pool['ir.config_parameter'].set_param(cr, uid, 'web.base.url', base)
|
||||
ICP = self.pool['ir.config_parameter']
|
||||
if not ICP.get_param(cr, uid, 'web.base.url.freeze'):
|
||||
ICP.set_param(cr, uid, 'web.base.url', base)
|
||||
cr.commit()
|
||||
except Exception:
|
||||
_logger.exception("Failed to update web.base.url configuration parameter")
|
||||
|
|
|
@ -56,7 +56,8 @@ openerp.base = function(instance) {
|
|||
});
|
||||
|
||||
};
|
||||
i.src = _.str.sprintf('%s/web/static/src/img/sep-a.gif', client.origin);
|
||||
var ts = new Date().getTime();
|
||||
i.src = _.str.sprintf('%s/web/static/src/img/sep-a.gif?%s', client.origin, ts);
|
||||
return d.promise();
|
||||
};
|
||||
if (instance.base.apps_client) {
|
||||
|
@ -96,7 +97,7 @@ openerp.base = function(instance) {
|
|||
client.replace(self.$el).
|
||||
done(function() {
|
||||
client.$el.removeClass('openerp');
|
||||
client.do_action(self.remote_action_id);
|
||||
client.do_action(self.remote_action_id, {hide_breadcrumb: true});
|
||||
});
|
||||
}).
|
||||
fail(function(client) {
|
||||
|
|
|
@ -114,6 +114,12 @@ class test_expression(common.TransactionCase):
|
|||
# Test2: inheritance + relational fields
|
||||
user_ids = users_obj.search(cr, uid, [('child_ids.name', 'like', 'test_B')])
|
||||
self.assertEqual(set(user_ids), set([b1]), 'searching through inheritance failed')
|
||||
|
||||
# Special =? operator mean "is equal if right is set, otherwise always True"
|
||||
user_ids = users_obj.search(cr, uid, [('name', 'like', 'test'), ('parent_id', '=?', False)])
|
||||
self.assertEqual(set(user_ids), set([a, b1, b2]), '(x =? False) failed')
|
||||
user_ids = users_obj.search(cr, uid, [('name', 'like', 'test'), ('parent_id', '=?', b1_user.partner_id.id)])
|
||||
self.assertEqual(set(user_ids), set([b2]), '(x =? id) failed')
|
||||
|
||||
def test_20_auto_join(self):
|
||||
registry, cr, uid = self.registry, self.cr, self.uid
|
||||
|
|
|
@ -220,15 +220,7 @@ def quit_on_signals():
|
|||
os.unlink(config['pidfile'])
|
||||
sys.exit(0)
|
||||
|
||||
def configure_babel_localedata_path():
|
||||
# Workaround: py2exe and babel.
|
||||
if hasattr(sys, 'frozen'):
|
||||
import babel
|
||||
babel.localedata._dirname = os.path.join(os.path.dirname(sys.executable), 'localedata')
|
||||
|
||||
def main(args):
|
||||
os.environ["TZ"] = "UTC"
|
||||
|
||||
check_root_user()
|
||||
openerp.tools.config.parse_config(args)
|
||||
|
||||
|
@ -246,8 +238,6 @@ def main(args):
|
|||
|
||||
config = openerp.tools.config
|
||||
|
||||
configure_babel_localedata_path()
|
||||
|
||||
setup_signal_handlers(signal_handler)
|
||||
|
||||
if config["test_file"]:
|
||||
|
|
|
@ -34,6 +34,7 @@ import openerp
|
|||
import openerp.modules.db
|
||||
import openerp.modules.graph
|
||||
import openerp.modules.migration
|
||||
import openerp.modules.registry
|
||||
import openerp.osv as osv
|
||||
import openerp.tools as tools
|
||||
from openerp import SUPERUSER_ID
|
||||
|
@ -131,7 +132,7 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules=
|
|||
loaded_modules = []
|
||||
registry = openerp.registry(cr.dbname)
|
||||
migrations = openerp.modules.migration.MigrationManager(cr, graph)
|
||||
_logger.debug('loading %d packages...', len(graph))
|
||||
_logger.info('loading %d modules...', len(graph))
|
||||
|
||||
# Query manual fields for all models at once and save them on the registry
|
||||
# so the initialization code for each model does not have to do it
|
||||
|
@ -149,7 +150,7 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules=
|
|||
if skip_modules and module_name in skip_modules:
|
||||
continue
|
||||
|
||||
_logger.info('module %s: loading objects', package.name)
|
||||
_logger.debug('module %s: loading objects', package.name)
|
||||
migrations.migrate_module(package, 'pre')
|
||||
load_openerp_module(package.name)
|
||||
|
||||
|
|
|
@ -190,6 +190,10 @@ class RegistryManager(object):
|
|||
except KeyError:
|
||||
return cls.new(db_name, force_demo, status,
|
||||
update_module)
|
||||
finally:
|
||||
# set db tracker - cleaned up at the WSGI
|
||||
# dispatching phase in openerp.service.wsgi_server.application
|
||||
threading.current_thread().dbname = db_name
|
||||
|
||||
@classmethod
|
||||
def new(cls, db_name, force_demo=False, status=None,
|
||||
|
@ -231,6 +235,9 @@ class RegistryManager(object):
|
|||
|
||||
registry.ready = True
|
||||
|
||||
if update_module:
|
||||
# only in case of update, otherwise we'll have an infinite reload loop!
|
||||
cls.signal_registry_change(db_name)
|
||||
return registry
|
||||
|
||||
@classmethod
|
||||
|
|
|
@ -198,7 +198,7 @@ def normalize_domain(domain):
|
|||
expected -= 1
|
||||
else:
|
||||
expected += op_arity.get(token, 0) - 1
|
||||
assert expected == 0
|
||||
assert expected == 0, 'This domain is syntactically not correct: %s' % (domain)
|
||||
return result
|
||||
|
||||
|
||||
|
@ -597,6 +597,15 @@ class ExtendedLeaf(object):
|
|||
self.leaf = normalize_leaf(self.leaf)
|
||||
return True
|
||||
|
||||
def create_substitution_leaf(leaf, new_elements, new_model=None):
|
||||
""" From a leaf, create a new leaf (based on the new_elements tuple
|
||||
and new_model), that will have the same join context. Used to
|
||||
insert equivalent leafs in the processing stack. """
|
||||
if new_model is None:
|
||||
new_model = leaf.model
|
||||
new_join_context = [tuple(context) for context in leaf.join_context]
|
||||
new_leaf = ExtendedLeaf(new_elements, new_model, join_context=new_join_context)
|
||||
return new_leaf
|
||||
|
||||
class expression(object):
|
||||
""" Parse a domain expression
|
||||
|
@ -714,16 +723,6 @@ class expression(object):
|
|||
return ids + recursive_children(ids2, model, parent_field)
|
||||
return [(left, 'in', recursive_children(ids, left_model, parent or left_model._parent_name))]
|
||||
|
||||
def create_substitution_leaf(leaf, new_elements, new_model=None):
|
||||
""" From a leaf, create a new leaf (based on the new_elements tuple
|
||||
and new_model), that will have the same join context. Used to
|
||||
insert equivalent leafs in the processing stack. """
|
||||
if new_model is None:
|
||||
new_model = leaf.model
|
||||
new_join_context = [tuple(context) for context in leaf.join_context]
|
||||
new_leaf = ExtendedLeaf(new_elements, new_model, join_context=new_join_context)
|
||||
return new_leaf
|
||||
|
||||
def pop():
|
||||
""" Pop a leaf to process. """
|
||||
return self.stack.pop()
|
||||
|
@ -1152,7 +1151,8 @@ class expression(object):
|
|||
params = []
|
||||
else:
|
||||
# '=?' behaves like '=' in other cases
|
||||
query, params = self.__leaf_to_sql((left, '=', right), model)
|
||||
query, params = self.__leaf_to_sql(
|
||||
create_substitution_leaf(eleaf, (left, '=', right), model))
|
||||
|
||||
elif left == 'id':
|
||||
query = '%s.id %s %%s' % (table_alias, operator)
|
||||
|
|
|
@ -1028,7 +1028,7 @@ class BaseModel(object):
|
|||
'required': bool(field['required']),
|
||||
'readonly': bool(field['readonly']),
|
||||
'domain': eval(field['domain']) if field['domain'] else None,
|
||||
'size': field['size'],
|
||||
'size': field['size'] or None,
|
||||
'ondelete': field['on_delete'],
|
||||
'translate': (field['translate']),
|
||||
'manual': True,
|
||||
|
@ -4451,7 +4451,6 @@ class BaseModel(object):
|
|||
upd1 += ",%s,(now() at time zone 'UTC'),%s,(now() at time zone 'UTC')"
|
||||
upd2.extend((user, user))
|
||||
cr.execute('insert into "'+self._table+'" (id'+upd0+") values ("+str(id_new)+upd1+')', tuple(upd2))
|
||||
self.check_access_rule(cr, user, [id_new], 'create', context=context)
|
||||
upd_todo.sort(lambda x, y: self._columns[x].priority-self._columns[y].priority)
|
||||
|
||||
if self._parent_store and not context.get('defer_parent_store_computation'):
|
||||
|
@ -4504,6 +4503,7 @@ class BaseModel(object):
|
|||
self.name_get(cr, user, [id_new], context=context)[0][1] + \
|
||||
"' " + _("created.")
|
||||
self.log(cr, user, id_new, message, True, context=context)
|
||||
self.check_access_rule(cr, user, [id_new], 'create', context=context)
|
||||
self.create_workflow(cr, user, [id_new], context=context)
|
||||
return id_new
|
||||
|
||||
|
|
|
@ -49,6 +49,19 @@ _logger = logging.getLogger(__name__)
|
|||
|
||||
encoding = 'utf-8'
|
||||
|
||||
def select_fontname(fontname, default_fontname):
|
||||
if fontname not in pdfmetrics.getRegisteredFontNames()\
|
||||
or fontname not in pdfmetrics.standardFonts:
|
||||
# let reportlab attempt to find it
|
||||
try:
|
||||
pdfmetrics.getFont(fontname)
|
||||
except Exception:
|
||||
_logger.warning('Could not locate font %s, substituting default: %s',
|
||||
fontname, default_fontname)
|
||||
fontname = default_fontname
|
||||
return fontname
|
||||
|
||||
|
||||
def _open_image(filename, path=None):
|
||||
"""Attempt to open a binary file and return the descriptor
|
||||
"""
|
||||
|
@ -159,7 +172,12 @@ class _rml_styles(object,):
|
|||
for attr in ['textColor', 'backColor', 'bulletColor', 'borderColor']:
|
||||
if node.get(attr):
|
||||
data[attr] = color.get(node.get(attr))
|
||||
for attr in ['fontName', 'bulletFontName', 'bulletText']:
|
||||
for attr in ['bulletFontName', 'fontName']:
|
||||
if node.get(attr):
|
||||
fontname= select_fontname(node.get(attr), None)
|
||||
if fontname is not None:
|
||||
data['fontName'] = fontname
|
||||
for attr in ['bulletText']:
|
||||
if node.get(attr):
|
||||
data[attr] = node.get(attr)
|
||||
for attr in ['fontSize', 'leftIndent', 'rightIndent', 'spaceBefore', 'spaceAfter',
|
||||
|
@ -537,17 +555,7 @@ class _rml_canvas(object):
|
|||
self.canvas.drawPath(self.path, **utils.attr_get(node, [], {'fill':'bool','stroke':'bool'}))
|
||||
|
||||
def setFont(self, node):
|
||||
fontname = node.get('name')
|
||||
if fontname not in pdfmetrics.getRegisteredFontNames()\
|
||||
or fontname not in pdfmetrics.standardFonts:
|
||||
# let reportlab attempt to find it
|
||||
try:
|
||||
pdfmetrics.getFont(fontname)
|
||||
except Exception:
|
||||
_logger.debug('Could not locate font %s, substituting default: %s',
|
||||
fontname,
|
||||
self.canvas._fontname)
|
||||
fontname = self.canvas._fontname
|
||||
fontname = select_fontname(node.get('name'), self.canvas._fontname)
|
||||
return self.canvas.setFont(fontname, utils.unit_get(node.get('size')))
|
||||
|
||||
def render(self, node):
|
||||
|
|
|
@ -30,6 +30,7 @@ cron jobs, for all databases of a single OpenERP server instance.
|
|||
import logging
|
||||
import threading
|
||||
import time
|
||||
from datetime import datetime
|
||||
|
||||
import openerp
|
||||
|
||||
|
@ -56,6 +57,12 @@ def start_service():
|
|||
threads it spawns are not marked daemon).
|
||||
|
||||
"""
|
||||
|
||||
# Force call to strptime just before starting the cron thread
|
||||
# to prevent time.strptime AttributeError within the thread.
|
||||
# See: http://bugs.python.org/issue7980
|
||||
datetime.strptime('2012-01-01', '%Y-%m-%d')
|
||||
|
||||
for i in range(openerp.tools.config['max_cron_threads']):
|
||||
def target():
|
||||
cron_runner(i)
|
||||
|
|
|
@ -197,18 +197,26 @@ def exp_drop(db_name):
|
|||
return True
|
||||
|
||||
@contextlib.contextmanager
|
||||
def _set_pg_password_in_environment():
|
||||
""" On Win32, pg_dump (and pg_restore) require that
|
||||
:envvar:`PGPASSWORD` be set
|
||||
def _set_pg_password_in_environment(self):
|
||||
""" On systems where pg_restore/pg_dump require an explicit
|
||||
password (i.e. when not connecting via unix sockets, and most
|
||||
importantly on Windows), it is necessary to pass the PG user
|
||||
password in the environment or in a special .pgpass file.
|
||||
|
||||
This context management method handles setting
|
||||
:envvar:`PGPASSWORD` iif win32 and the envvar is not already
|
||||
:envvar:`PGPASSWORD` if it is not already
|
||||
set, and removing it afterwards.
|
||||
|
||||
See also http://www.postgresql.org/docs/8.4/static/libpq-envars.html
|
||||
|
||||
.. note:: This is not thread-safe, and should never be enabled for
|
||||
SaaS (giving SaaS users the super-admin password is not a good idea
|
||||
anyway)
|
||||
"""
|
||||
if os.name != 'nt' or os.environ.get('PGPASSWORD'):
|
||||
if os.environ.get('PGPASSWORD') or not tools.config['db_password']:
|
||||
yield
|
||||
else:
|
||||
os.environ['PGPASSWORD'] = openerp.tools.config['db_password']
|
||||
os.environ['PGPASSWORD'] = tools.config['db_password']
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
|
@ -234,7 +242,7 @@ def exp_dump(db_name):
|
|||
if not data or res:
|
||||
_logger.error(
|
||||
'DUMP DB: %s failed! Please verify the configuration of the database password on the server. '
|
||||
'It should be provided as a -w <PASSWD> command-line option, or as `db_password` in the '
|
||||
'You may need to create a .pgpass file for authentication, or specify `db_password` in the '
|
||||
'server configuration file.\n %s', db_name, data)
|
||||
raise Exception, "Couldn't dump database"
|
||||
_logger.info('DUMP DB successful: %s', db_name)
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
from functools import wraps
|
||||
import logging
|
||||
from psycopg2 import IntegrityError, errorcodes
|
||||
import random
|
||||
import threading
|
||||
import time
|
||||
|
||||
import openerp
|
||||
from openerp.tools.translate import translate
|
||||
|
@ -13,9 +15,16 @@ import security
|
|||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
PG_CONCURRENCY_ERRORS_TO_RETRY = (errorcodes.LOCK_NOT_AVAILABLE, errorcodes.SERIALIZATION_FAILURE, errorcodes.DEADLOCK_DETECTED)
|
||||
MAX_TRIES_ON_CONCURRENCY_FAILURE = 5
|
||||
|
||||
def dispatch(method, params):
|
||||
(db, uid, passwd ) = params[0:3]
|
||||
|
||||
# set uid tracker - cleaned up at the WSGI
|
||||
# dispatching phase in openerp.service.wsgi_server.application
|
||||
threading.current_thread().uid = uid
|
||||
|
||||
params = params[3:]
|
||||
if method == 'obj_list':
|
||||
raise NameError("obj_list has been discontinued via RPC as of 6.0, please query ir.model directly!")
|
||||
|
@ -94,37 +103,50 @@ def check(f):
|
|||
def _(src):
|
||||
return tr(src, 'code')
|
||||
|
||||
try:
|
||||
if openerp.registry(dbname)._init:
|
||||
raise openerp.exceptions.Warning('Currently, this database is not fully loaded and can not be used.')
|
||||
return f(dbname, *args, **kwargs)
|
||||
except IntegrityError, inst:
|
||||
registry = openerp.registry(dbname)
|
||||
for key in registry._sql_error.keys():
|
||||
if key in inst[0]:
|
||||
raise openerp.osv.orm.except_orm(_('Constraint Error'), tr(registry._sql_error[key], 'sql_constraint') or inst[0])
|
||||
if inst.pgcode in (errorcodes.NOT_NULL_VIOLATION, errorcodes.FOREIGN_KEY_VIOLATION, errorcodes.RESTRICT_VIOLATION):
|
||||
msg = _('The operation cannot be completed, probably due to the following:\n- deletion: you may be trying to delete a record while other records still reference it\n- creation/update: a mandatory field is not correctly set')
|
||||
_logger.debug("IntegrityError", exc_info=True)
|
||||
try:
|
||||
errortxt = inst.pgerror.replace('«','"').replace('»','"')
|
||||
if '"public".' in errortxt:
|
||||
context = errortxt.split('"public".')[1]
|
||||
model_name = table = context.split('"')[1]
|
||||
else:
|
||||
last_quote_end = errortxt.rfind('"')
|
||||
last_quote_begin = errortxt.rfind('"', 0, last_quote_end)
|
||||
model_name = table = errortxt[last_quote_begin+1:last_quote_end].strip()
|
||||
model = table.replace("_",".")
|
||||
model_obj = registry.get(model)
|
||||
if model_obj:
|
||||
model_name = model_obj._description or model_obj._name
|
||||
msg += _('\n\n[object with reference: %s - %s]') % (model_name, model)
|
||||
except Exception:
|
||||
pass
|
||||
raise openerp.osv.orm.except_orm(_('Integrity Error'), msg)
|
||||
else:
|
||||
raise openerp.osv.orm.except_orm(_('Integrity Error'), inst[0])
|
||||
tries = 0
|
||||
while True:
|
||||
try:
|
||||
if openerp.registry(dbname)._init:
|
||||
raise openerp.exceptions.Warning('Currently, this database is not fully loaded and can not be used.')
|
||||
return f(dbname, *args, **kwargs)
|
||||
except OperationalError, e:
|
||||
# Automatically retry the typical transaction serialization errors
|
||||
if e.pgcode not in PG_CONCURRENCY_ERRORS_TO_RETRY:
|
||||
raise
|
||||
if tries >= MAX_TRIES_ON_CONCURRENCY_FAILURE:
|
||||
_logger.warning("%s, maximum number of tries reached" % errorcodes.lookup(e.pgcode))
|
||||
raise
|
||||
wait_time = random.uniform(0.0, 2 ** tries)
|
||||
tries += 1
|
||||
_logger.info("%s, retry %d/%d in %.04f sec..." % (errorcodes.lookup(e.pgcode), tries, MAX_TRIES_ON_CONCURRENCY_FAILURE, wait_time))
|
||||
time.sleep(wait_time)
|
||||
except IntegrityError, inst:
|
||||
registry = openerp.registry(dbname)
|
||||
for key in registry._sql_error.keys():
|
||||
if key in inst[0]:
|
||||
raise openerp.osv.orm.except_orm(_('Constraint Error'), tr(registry._sql_error[key], 'sql_constraint') or inst[0])
|
||||
if inst.pgcode in (errorcodes.NOT_NULL_VIOLATION, errorcodes.FOREIGN_KEY_VIOLATION, errorcodes.RESTRICT_VIOLATION):
|
||||
msg = _('The operation cannot be completed, probably due to the following:\n- deletion: you may be trying to delete a record while other records still reference it\n- creation/update: a mandatory field is not correctly set')
|
||||
_logger.debug("IntegrityError", exc_info=True)
|
||||
try:
|
||||
errortxt = inst.pgerror.replace('«','"').replace('»','"')
|
||||
if '"public".' in errortxt:
|
||||
context = errortxt.split('"public".')[1]
|
||||
model_name = table = context.split('"')[1]
|
||||
else:
|
||||
last_quote_end = errortxt.rfind('"')
|
||||
last_quote_begin = errortxt.rfind('"', 0, last_quote_end)
|
||||
model_name = table = errortxt[last_quote_begin+1:last_quote_end].strip()
|
||||
model = table.replace("_",".")
|
||||
model_obj = registry.get(model)
|
||||
if model_obj:
|
||||
model_name = model_obj._description or model_obj._name
|
||||
msg += _('\n\n[object with reference: %s - %s]') % (model_name, model)
|
||||
except Exception:
|
||||
pass
|
||||
raise openerp.osv.orm.except_orm(_('Integrity Error'), msg)
|
||||
else:
|
||||
raise openerp.osv.orm.except_orm(_('Integrity Error'), inst[0])
|
||||
|
||||
return wrapper
|
||||
|
||||
|
|
|
@ -388,9 +388,19 @@ class WorkerBaseWSGIServer(werkzeug.serving.BaseWSGIServer):
|
|||
|
||||
class WorkerCron(Worker):
|
||||
""" Cron workers """
|
||||
|
||||
def __init__(self, multi):
|
||||
super(WorkerCron, self).__init__(multi)
|
||||
# process_work() below process a single database per call.
|
||||
# The variable db_index is keeping track of the next database to
|
||||
# process.
|
||||
self.db_index = 0
|
||||
|
||||
def sleep(self):
|
||||
interval = 60 + self.pid % 10 # chorus effect
|
||||
time.sleep(interval)
|
||||
# Really sleep once all the databases have been processed.
|
||||
if self.db_index == 0:
|
||||
interval = 60 + self.pid % 10 # chorus effect
|
||||
time.sleep(interval)
|
||||
|
||||
def process_work(self):
|
||||
rpc_request = logging.getLogger('openerp.netsvc.rpc.request')
|
||||
|
@ -400,7 +410,9 @@ class WorkerCron(Worker):
|
|||
db_names = config['db_name'].split(',')
|
||||
else:
|
||||
db_names = openerp.service.db.exp_list(True)
|
||||
for db_name in db_names:
|
||||
if len(db_names):
|
||||
self.db_index = (self.db_index + 1) % len(db_names)
|
||||
db_name = db_names[self.db_index]
|
||||
if rpc_request_flag:
|
||||
start_time = time.time()
|
||||
start_rss, start_vms = psutil.Process(os.getpid()).get_memory_info()
|
||||
|
@ -419,8 +431,14 @@ class WorkerCron(Worker):
|
|||
end_rss, end_vms = psutil.Process(os.getpid()).get_memory_info()
|
||||
logline = '%s time:%.3fs mem: %sk -> %sk (diff: %sk)' % (db_name, end_time - start_time, start_vms / 1024, end_vms / 1024, (end_vms - start_vms)/1024)
|
||||
_logger.debug("WorkerCron (%s) %s", self.pid, logline)
|
||||
# TODO Each job should be considered as one request instead of each run
|
||||
self.request_count += 1
|
||||
|
||||
self.request_count += 1
|
||||
if self.request_count >= self.request_max and self.request_max < len(db_names):
|
||||
_logger.error("There are more dabatases to process than allowed "
|
||||
"by the `limit_request` configuration variable: %s more.",
|
||||
len(db_names) - self.request_max)
|
||||
else:
|
||||
self.db_index = 0
|
||||
|
||||
def start(self):
|
||||
Worker.start(self)
|
||||
|
|
|
@ -390,6 +390,16 @@ def register_rpc_endpoint(endpoint, handler):
|
|||
|
||||
def application_unproxied(environ, start_response):
|
||||
""" WSGI entry point."""
|
||||
# cleanup db/uid trackers - they're set at HTTP dispatch in
|
||||
# web.session.OpenERPSession.send() and at RPC dispatch in
|
||||
# openerp.service.web_services.objects_proxy.dispatch().
|
||||
# /!\ The cleanup cannot be done at the end of this `application`
|
||||
# method because werkzeug still produces relevant logging afterwards
|
||||
if hasattr(threading.current_thread(), 'uid'):
|
||||
del threading.current_thread().uid
|
||||
if hasattr(threading.current_thread(), 'dbname'):
|
||||
del threading.current_thread().dbname
|
||||
|
||||
openerp.service.start_internal()
|
||||
|
||||
# Try all handlers until one returns some result (i.e. not None).
|
||||
|
@ -401,7 +411,6 @@ def application_unproxied(environ, start_response):
|
|||
continue
|
||||
return result
|
||||
|
||||
|
||||
# We never returned from the loop.
|
||||
response = 'No handler found.\n'
|
||||
start_response('404 Not Found', [('Content-Type', 'text/plain'), ('Content-Length', str(len(response)))])
|
||||
|
|
|
@ -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-2013 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
|
||||
|
@ -36,7 +36,6 @@ import psycopg2.extensions
|
|||
from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT, ISOLATION_LEVEL_READ_COMMITTED, ISOLATION_LEVEL_REPEATABLE_READ
|
||||
from psycopg2.pool import PoolError
|
||||
from psycopg2.psycopg1 import cursor as psycopg1cursor
|
||||
from threading import currentThread
|
||||
|
||||
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
|
||||
|
||||
|
@ -393,7 +392,7 @@ class ConnectionPool(object):
|
|||
def borrow(self, dsn):
|
||||
self._debug('Borrow connection to %r', dsn)
|
||||
|
||||
# free leaked connections
|
||||
# free dead and leaked connections
|
||||
for i, (cnx, _) in tools.reverse_enumerate(self._connections):
|
||||
if cnx.closed:
|
||||
self._connections.pop(i)
|
||||
|
@ -407,6 +406,14 @@ class ConnectionPool(object):
|
|||
|
||||
for i, (cnx, used) in enumerate(self._connections):
|
||||
if not used and dsn_are_equals(cnx.dsn, dsn):
|
||||
try:
|
||||
cnx.reset()
|
||||
except psycopg2.OperationalError:
|
||||
self._debug('Cannot reset connection at index %d: %r', i, cnx.dsn)
|
||||
# psycopg2 2.4.4 and earlier do not allow closing a closed connection
|
||||
if not cnx.closed:
|
||||
cnx.close()
|
||||
continue
|
||||
self._connections.pop(i)
|
||||
self._connections.append((cnx, True))
|
||||
self._debug('Existing connection found at index %d', i)
|
||||
|
@ -507,7 +514,6 @@ def db_connect(db_name):
|
|||
global _Pool
|
||||
if _Pool is None:
|
||||
_Pool = ConnectionPool(int(tools.config['db_maxconn']))
|
||||
currentThread().dbname = db_name
|
||||
return Connection(_Pool, db_name)
|
||||
|
||||
def close_db(db_name):
|
||||
|
@ -515,9 +521,6 @@ def close_db(db_name):
|
|||
global _Pool
|
||||
if _Pool:
|
||||
_Pool.close_all(dsn(db_name))
|
||||
ct = currentThread()
|
||||
if hasattr(ct, 'dbname'):
|
||||
delattr(ct, 'dbname')
|
||||
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -83,6 +83,8 @@ def image_resize_image(base64_source, size=(1024, 1024), encoding='base64', file
|
|||
if image.size != size:
|
||||
# If you need faster thumbnails you may use use Image.NEAREST
|
||||
image = ImageOps.fit(image, size, Image.ANTIALIAS)
|
||||
if image.mode not in ["1", "L", "P", "RGB", "RGBA"]:
|
||||
image = image.convert("RGB")
|
||||
|
||||
background_stream = StringIO.StringIO()
|
||||
image.save(background_stream, filetype)
|
||||
|
|
|
@ -50,7 +50,7 @@ def html_sanitize(src):
|
|||
src = ustr(src, errors='replace')
|
||||
|
||||
# html encode email tags
|
||||
part = re.compile(r"(<[^<>]+@[^<>]+>)", re.IGNORECASE | re.DOTALL)
|
||||
part = re.compile(r"(<(([^a<>]|a[^<>\s])[^<>]*)@[^<>]+>)", re.IGNORECASE | re.DOTALL)
|
||||
src = part.sub(lambda m: cgi.escape(m.group(1)), src)
|
||||
|
||||
# some corner cases make the parser crash (such as <SCRIPT/XSS SRC=\"http://ha.ckers.org/xss.js\"></SCRIPT> in test_mail)
|
||||
|
@ -185,6 +185,8 @@ def html2plaintext(html, body_id=None, encoding='utf-8'):
|
|||
url_index.append(url)
|
||||
|
||||
html = ustr(etree.tostring(tree, encoding=encoding))
|
||||
# \r char is converted into , must remove it
|
||||
html = html.replace(' ', '')
|
||||
|
||||
html = html.replace('<strong>', '*').replace('</strong>', '*')
|
||||
html = html.replace('<b>', '*').replace('</b>', '*')
|
||||
|
|
|
@ -138,6 +138,7 @@ def file_open(name, mode="r", subdir='addons', pathinfo=False):
|
|||
# Is it below 'addons_path' or 'root_path'?
|
||||
name = os.path.normcase(os.path.normpath(name))
|
||||
for root in adps + [rtp]:
|
||||
root = os.path.normcase(os.path.normpath(root)) + os.sep
|
||||
if name.startswith(root):
|
||||
base = root.rstrip(os.sep)
|
||||
name = name[len(base) + 1:]
|
||||
|
|
|
@ -550,6 +550,8 @@ def trans_parse_view(de):
|
|||
res.append(de.get('sum').encode("utf8"))
|
||||
if de.get("confirm"):
|
||||
res.append(de.get('confirm').encode("utf8"))
|
||||
if de.get("placeholder"):
|
||||
res.append(de.get('placeholder').encode("utf8"))
|
||||
for n in de:
|
||||
res.extend(trans_parse_view(n))
|
||||
return res
|
||||
|
|
|
@ -25,7 +25,6 @@ def run(args):
|
|||
openerp.cli.server.check_root_user()
|
||||
openerp.netsvc.init_logger()
|
||||
#openerp.cli.server.report_configuration()
|
||||
openerp.cli.server.configure_babel_localedata_path()
|
||||
openerp.cli.server.setup_signal_handlers(openerp.cli.server.signal_handler)
|
||||
import openerp.addons.base
|
||||
if args.database:
|
||||
|
|
|
@ -49,7 +49,6 @@ def run(args):
|
|||
openerp.cli.server.check_root_user()
|
||||
openerp.netsvc.init_logger()
|
||||
#openerp.cli.server.report_configuration()
|
||||
openerp.cli.server.configure_babel_localedata_path()
|
||||
|
||||
target = openerp.service.wsgi_server.serve
|
||||
if not args.gevent:
|
||||
|
|
|
@ -291,9 +291,10 @@ Function .onInit
|
|||
!insertmacro MUI_LANGDLL_DISPLAY
|
||||
|
||||
ClearErrors
|
||||
EnumRegKey $0 HKLM "SOFTWARE\PostgreSQL" 0
|
||||
EnumRegKey $0 HKLM "SOFTWARE\PostgreSQL\Installations" 0
|
||||
IfErrors DoInstallPostgreSQL 0
|
||||
StrCpy $HasPostgreSQL 1
|
||||
StrCmp $0 "" DoInstallPostgreSQL
|
||||
StrCpy $HasPostgreSQL 1
|
||||
|
||||
DoInstallPostgreSQL:
|
||||
FunctionEnd
|
||||
|
|
14
setup.py
14
setup.py
|
@ -35,7 +35,15 @@ def data():
|
|||
r["Microsoft.VC90.CRT"] = glob.glob('C:\Microsoft.VC90.CRT\*.*')
|
||||
|
||||
import babel
|
||||
r["localedata"] = glob.glob(os.path.join(os.path.dirname(babel.__file__), "localedata", '*'))
|
||||
# Add data, but also some .py files py2exe won't include automatically.
|
||||
# TODO This should probably go under `packages`, instead of `data`,
|
||||
# but this will work fine (especially since we don't use the ZIP file
|
||||
# approach).
|
||||
r["babel/localedata"] = glob.glob(os.path.join(os.path.dirname(babel.__file__), "localedata", '*'))
|
||||
others = ['global.dat', 'numbers.py', 'support.py']
|
||||
r["babel"] = map(lambda f: os.path.join(os.path.dirname(babel.__file__), f), others)
|
||||
others = ['frontend.py', 'mofile.py']
|
||||
r["babel/messages"] = map(lambda f: os.path.join(os.path.dirname(babel.__file__), "messages", f), others)
|
||||
|
||||
import pytz
|
||||
tzdir = os.path.dirname(pytz.__file__)
|
||||
|
@ -66,7 +74,7 @@ def py2exe_options():
|
|||
'options' : {
|
||||
"py2exe": {
|
||||
"skip_archive": 1,
|
||||
"optimize": 2,
|
||||
"optimize": 0, # keep the assert running, because the integrated tests rely on them.
|
||||
"dist_dir": 'dist',
|
||||
"packages": [ "DAV", "HTMLParser", "PIL", "asynchat", "asyncore", "commands", "dateutil", "decimal", "docutils", "email", "encodings", "imaplib", "jinja2", "lxml", "lxml._elementpath", "lxml.builder", "lxml.etree", "lxml.objectify", "mako", "openerp", "poplib", "pychart", "pydot", "pyparsing", "pytz", "reportlab", "select", "simplejson", "smtplib", "uuid", "vatnumber", "vobject", "xml", "xml.dom", "yaml", ],
|
||||
"excludes" : ["Tkconstants","Tkinter","tcl"],
|
||||
|
@ -118,7 +126,7 @@ setuptools.setup(
|
|||
'mock',
|
||||
'PIL', # windows binary http://www.lfd.uci.edu/~gohlke/pythonlibs/
|
||||
'psutil', # windows binary code.google.com/p/psutil/downloads/list
|
||||
'psycopg2',
|
||||
'psycopg2 >= 2.2',
|
||||
'pydot',
|
||||
'pyparsing < 2',
|
||||
'python-dateutil < 2',
|
||||
|
|
Loading…
Reference in New Issue