diff --git a/.bzrignore b/.bzrignore index 1f9c25fe409..9b867797a34 100644 --- a/.bzrignore +++ b/.bzrignore @@ -1,3 +1,5 @@ -./bin/addons/* -./bin/filestore* - +*.pyc +.*.swp +.bzrignore +bin/addons/* +bin/filestore* diff --git a/bin/addons/__init__.py b/bin/addons/__init__.py index 72fe0c68af1..a08b37d335b 100644 --- a/bin/addons/__init__.py +++ b/bin/addons/__init__.py @@ -23,7 +23,6 @@ import os, sys, imp from os.path import join as opj import itertools -# from sets import Set import zipimport import osv @@ -69,6 +68,26 @@ class Graph(dict): father.addChild(name) else: Node(name, self) + + def update_from_db(self, cr): + # update the graph with values from the database (if exist) + ## First, we set the default values for each package in graph + additional_data = dict.fromkeys(self.keys(), {'id': 0, 'state': 'uninstalled', 'dbdemo': False, 'installed_version': None}) + ## Then we get the values from the database + cr.execute('SELECT name, id, state, demo AS dbdemo, latest_version AS installed_version' + ' FROM ir_module_module' + ' WHERE name in (%s)' % (','.join(['%s'] * len(self))), + additional_data.keys() + ) + + ## and we update the default values with values from the database + additional_data.update(dict([(x.pop('name'), x) for x in cr.dictfetchall()])) + + for package in self.values(): + for k, v in additional_data[package.name].items(): + setattr(package, k, v) + + def __iter__(self): level = 0 @@ -80,9 +99,7 @@ class Graph(dict): yield module level += 1 - class Singleton(object): - def __new__(cls, name, graph): if name in graph: inst = graph[name] @@ -97,57 +114,53 @@ class Node(Singleton): def __init__(self, name, graph): self.graph = graph - if not hasattr(self, 'childs'): - self.childs = [] + if not hasattr(self, 'children'): + self.children = [] if not hasattr(self, 'depth'): self.depth = 0 def addChild(self, name): node = Node(name, self.graph) node.depth = self.depth + 1 - if node not in self.childs: - self.childs.append(node) + if node not in self.children: + self.children.append(node) for attr in ('init', 'update', 'demo'): if hasattr(self, attr): setattr(node, attr, True) - self.childs.sort(lambda x, y: cmp(x.name, y.name)) - - def hasChild(self, name): - return Node(name, self.graph) in self.childs or \ - bool([c for c in self.childs if c.hasChild(name)]) + self.children.sort(lambda x, y: cmp(x.name, y.name)) def __setattr__(self, name, value): super(Singleton, self).__setattr__(name, value) if name in ('init', 'update', 'demo'): tools.config[name][self.name] = 1 - for child in self.childs: + for child in self.children: setattr(child, name, value) if name == 'depth': - for child in self.childs: + for child in self.children: setattr(child, name, value + 1) def __iter__(self): - return itertools.chain(iter(self.childs), *map(iter, self.childs)) - + return itertools.chain(iter(self.children), *map(iter, self.children)) + def __str__(self): return self._pprint() def _pprint(self, depth=0): s = '%s\n' % self.name - for c in self.childs: + for c in self.children: s += '%s`-> %s' % (' ' * depth, c._pprint(depth+1)) return s -def get_module_path(module): +def get_module_path(module, downloaded=False): """Return the path of the given module.""" - if os.path.exists(opj(ad, module)) or os.path.exists(opj(ad, '%s.zip' % module)): return opj(ad, module) if os.path.exists(opj(_ad, module)) or os.path.exists(opj(_ad, '%s.zip' % module)): return opj(_ad, module) - + if downloaded: + return opj(_ad, module) logger.notifyChannel('init', netsvc.LOG_WARNING, 'module %s: module not found' % (module,)) return False @@ -334,6 +347,8 @@ def upgrade_graph(graph, cr, module_list, force=None): packages.append((package, deps, data)) packages.pop(0) + graph.update_from_db(cr) + for package in later: unmet_deps = filter(lambda p: p not in graph, dependencies[package]) logger.notifyChannel('init', netsvc.LOG_ERROR, 'module %s: Unmet dependencies: %s' % (package, ', '.join(unmet_deps))) @@ -552,24 +567,6 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, **kwargs): statusi = 0 pool = pooler.get_pool(cr.dbname) - - # update the graph with values from the database (if exist) - ## First, we set the default values for each package in graph - additional_data = dict.fromkeys([p.name for p in graph], {'id': 0, 'state': 'uninstalled', 'dbdemo': False, 'installed_version': None}) - ## Then we get the values from the database - cr.execute('SELECT name, id, state, demo AS dbdemo, latest_version AS installed_version' - ' FROM ir_module_module' - ' WHERE name in (%s)' % (','.join(['%s'] * len(graph))), - additional_data.keys() - ) - - ## and we update the default values with values from the database - additional_data.update(dict([(x.pop('name'), x) for x in cr.dictfetchall()])) - - for package in graph: - for k, v in additional_data[package.name].items(): - setattr(package, k, v) - migrations = MigrationManager(cr, graph) has_updates = False @@ -596,14 +593,16 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, **kwargs): idref = {} status['progress'] = (float(statusi)+0.4) / len(graph) + + mode = 'update' + if hasattr(package, 'init') or package.state == 'to install': + mode = 'init' + if hasattr(package, 'init') or hasattr(package, 'update') or package.state in ('to install', 'to upgrade'): has_updates = True init_module_objects(cr, m, modules) for kind in ('init', 'update'): for filename in package.data.get('%s_xml' % kind, []): - mode = 'update' - if hasattr(package, 'init') or package.state == 'to install': - mode = 'init' logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s: loading %s' % (m, filename)) name, ext = os.path.splitext(filename) fp = tools.file_open(opj(m, filename)) @@ -648,9 +647,7 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, **kwargs): if hasattr(package, kind): delattr(package, kind) - statusi += 1 - cr.execute('select model from ir_model where state=%s', ('manual',)) for model in cr.dictfetchall(): @@ -750,7 +747,7 @@ def load_modules(db, force_demo=False, status=None, update_module=False): cr.execute('delete from ir_model_data where noupdate=%s and module=%s', (False, mod_name,)) cr.commit() # - # TODO: remove menu without actions of childs + # TODO: remove menu without actions of children # while True: cr.execute('''delete from @@ -774,4 +771,3 @@ def load_modules(db, force_demo=False, status=None, update_module=False): # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: - diff --git a/bin/addons/base/base.sql b/bin/addons/base/base.sql index 50d793b6dd5..8dfc8d8f765 100644 --- a/bin/addons/base/base.sql +++ b/bin/addons/base/base.sql @@ -11,7 +11,7 @@ create table ir_values id serial, name varchar(128) not null, key varchar(128) not null, - key2 varchar(128) not null, + key2 varchar(256) not null, model varchar(128) not null, value text, meta text default NULL, @@ -55,7 +55,7 @@ CREATE TABLE ir_model_fields ( CREATE TABLE ir_actions ( id serial NOT NULL, name varchar(64) DEFAULT ''::varchar NOT NULL, - "type" varchar(64) DEFAULT 'window'::varchar NOT NULL, + "type" varchar(32) DEFAULT 'window'::varchar NOT NULL, usage varchar(32) DEFAULT null, primary key(id) ); @@ -64,7 +64,7 @@ CREATE TABLE ir_act_window ( view_id integer, res_model varchar(64), view_type varchar(16), - "domain" varchar(127), + "domain" varchar(250), primary key(id) ) INHERITS (ir_actions); @@ -72,8 +72,8 @@ INHERITS (ir_actions); CREATE TABLE ir_act_report_xml ( model varchar(64) NOT NULL, report_name varchar(64) NOT NULL, - report_xsl varchar(64), - report_xml varchar(64), + report_xsl varchar(256), + report_xml varchar(256), auto boolean default true, primary key(id) ) @@ -140,7 +140,7 @@ CREATE TABLE res_users ( name varchar(64) not null, active boolean default True, login varchar(64) NOT NULL UNIQUE, - password varchar(32) default null, + password varchar(64) default null, context_tz varchar(64) default null, signature text, -- action_id int references ir_act_window on delete set null, @@ -151,14 +151,14 @@ alter table res_users add constraint res_users_login_uniq unique (login); CREATE TABLE res_groups ( id serial NOT NULL, - name varchar(32) NOT NULL, + name varchar(64) NOT NULL, primary key(id) ); create table res_roles ( id serial NOT NULL, parent_id int references res_roles on delete set null, - name varchar(32) NOT NULL, + name varchar(64) NOT NULL, primary key(id) ); diff --git a/bin/addons/base/base_data.xml b/bin/addons/base/base_data.xml index d87ee37273f..5004f7fc981 100644 --- a/bin/addons/base/base_data.xml +++ b/bin/addons/base/base_data.xml @@ -2748,7 +2748,6 @@ COP 0.01 4 - @@ -2756,7 +2755,6 @@ CZK 0.01 4 - @@ -2777,7 +2775,6 @@ HUF 0.01 4 - @@ -2785,7 +2782,6 @@ IDR 0.01 4 - @@ -2832,7 +2828,6 @@ PLN 0.01 4 - @@ -2840,7 +2835,6 @@ SEK 0.01 4 - @@ -2860,9 +2854,20 @@ ARS 0.01 4 - - + + + INR + Rs + 0.01 + 4 + + + 0.634 + + + + Tiny sprl diff --git a/bin/addons/base/ir/ir.xml b/bin/addons/base/ir/ir.xml index 6f55a5cdb6e..0f164e637df 100644 --- a/bin/addons/base/ir/ir.xml +++ b/bin/addons/base/ir/ir.xml @@ -1234,8 +1234,8 @@ - - + + diff --git a/bin/addons/base/ir/ir_cron.py b/bin/addons/base/ir/ir_cron.py index 8076ce44896..eecb551fcd3 100644 --- a/bin/addons/base/ir/ir_cron.py +++ b/bin/addons/base/ir/ir_cron.py @@ -77,47 +77,47 @@ class ir_cron(osv.osv, netsvc.Agent): def _poolJobs(self, db_name, check=False): try: db, pool = pooler.get_db_and_pool(db_name) - if pool._init: - # retry in a few minutes - self.setAlarm(self._poolJobs, int(time.time())+10*60, [db_name]) - cr = db.cursor() except: return False - - now = DateTime.now() - #FIXME: multidb. Solution: a l'instanciation d'une nouvelle connection bd (ds pooler) fo que j'instancie - # un nouveau pooljob avec comme parametre la bd - try: - cr.execute('select * from ir_cron where numbercall<>0 and active and nextcall<=now() order by priority') - for job in cr.dictfetchall(): - nextcall = DateTime.strptime(job['nextcall'], '%Y-%m-%d %H:%M:%S') - numbercall = job['numbercall'] + if pool._init: + # retry in a few minutes + next_call = 600 + else: + next_call = next_wait + cr = db.cursor() + now = DateTime.now() + try: + cr.execute('select * from ir_cron where numbercall<>0 and active and nextcall<=now() order by priority') + for job in cr.dictfetchall(): + nextcall = DateTime.strptime(job['nextcall'], '%Y-%m-%d %H:%M:%S') + numbercall = job['numbercall'] - ok = False - while nextcall 0: - numbercall -= 1 - if not ok or job['doall']: - self._callback(cr, job['user_id'], job['model'], job['function'], job['args']) - if numbercall: - nextcall += _intervalTypes[job['interval_type']](job['interval_number']) - ok = True - addsql='' - if not numbercall: - addsql = ', active=False' - cr.execute("update ir_cron set nextcall=%s, numbercall=%s"+addsql+" where id=%s", (nextcall.strftime('%Y-%m-%d %H:%M:%S'), numbercall, job['id'])) - cr.commit() + ok = False + while nextcall < now and numbercall: + if numbercall > 0: + numbercall -= 1 + if not ok or job['doall']: + self._callback(cr, job['user_id'], job['model'], job['function'], job['args']) + if numbercall: + nextcall += _intervalTypes[job['interval_type']](job['interval_number']) + ok = True + addsql='' + if not numbercall: + addsql = ', active=False' + cr.execute("update ir_cron set nextcall=%s, numbercall=%s"+addsql+" where id=%s", (nextcall.strftime('%Y-%m-%d %H:%M:%S'), numbercall, job['id'])) + cr.commit() except: print "Exception!" - finally: - cr.close() + finally: + cr.close() + # - # Can be improved to do at the min(min(nextcalls), time()+next_wait) + # Can be improved to do at the min(min(nextcalls), time()+next_call) # But is this an improvement ? # if not check: - self.setAlarm(self._poolJobs, int(time.time())+next_wait, [db_name]) + self.setAlarm(self._poolJobs, int(time.time()) + next_call, db_name, db_name) return None ir_cron() diff --git a/bin/addons/base/ir/ir_model.py b/bin/addons/base/ir/ir_model.py index 25507e2496e..958dd4845db 100644 --- a/bin/addons/base/ir/ir_model.py +++ b/bin/addons/base/ir/ir_model.py @@ -437,14 +437,16 @@ class ir_model_data(osv.osv): if (not xml_id) and (not self.doinit): return False action_id = False + if xml_id: cr.execute('select id,res_id from ir_model_data where module=%s and name=%s', (module,xml_id)) results = cr.fetchall() for action_id2,res_id2 in results: - cr.execute('select id from '+self.pool.get(model)._table+' where id=%s', (res_id2,)) + cr.execute('select id from '+model_obj._table+' where id=%s', (res_id2,)) result3 = cr.fetchone() if not result3: cr.execute('delete from ir_model_data where id=%s', (action_id2,)) + res_id = False else: res_id,action_id = res_id2,action_id2 @@ -512,7 +514,7 @@ class ir_model_data(osv.osv): #self.pool.get(model).unlink(cr, uid, ids) for id in ids: self.unlink_mark[(model, id)]=False - cr.execute('delete from ir_model_data where res_id=%s and model=\'%s\'', (id,model)) + cr.execute('delete from ir_model_data where res_id=%s and model=%s', (id, model)) return True def ir_set(self, cr, uid, key, key2, name, models, value, replace=True, isobject=False, meta=None, xml_id=False): @@ -563,18 +565,20 @@ class ir_model_data(osv.osv): cr.commit() if not config.get('import_partial', False): - for (model,id) in self.unlink_mark.keys(): + logger = netsvc.Logger() + for (model, res_id) in self.unlink_mark.keys(): if self.pool.get(model): - logger = netsvc.Logger() - logger.notifyChannel('init', netsvc.LOG_INFO, 'Deleting %s@%s' % (id, model)) + logger.notifyChannel('init', netsvc.LOG_INFO, 'Deleting %s@%s' % (res_id, model)) try: - self.pool.get(model).unlink(cr, uid, [id]) - if self.unlink_mark[(model,id)]: - self.unlink(cr, uid, [self.unlink_mark[(model,id)]]) - cr.execute('DELETE FROM ir_values WHERE value=%s', (model+','+str(id),)) + self.pool.get(model).unlink(cr, uid, [res_id]) + id = self.unlink_mark[(model, res_id)] + if id: + self.unlink(cr, uid, [id]) + cr.execute('DELETE FROM ir_values WHERE value=%s', ('%s,%s' % (model, res_id),)) cr.commit() except: - logger.notifyChannel('init', netsvc.LOG_ERROR, 'Could not delete id: %d of model %s\tThere should be some relation that points to this resource\tYou should manually fix this and restart --update=module' % (id, model)) + cr.rollback() + logger.notifyChannel('init', netsvc.LOG_ERROR, 'Could not delete id: %d of model %s\nThere should be some relation that points to this resource\nYou should manually fix this and restart --update=module' % (res_id, model)) return True ir_model_data() diff --git a/bin/addons/base/ir/ir_rule.py b/bin/addons/base/ir/ir_rule.py index 18fd97c4bd9..fdfa82ec002 100644 --- a/bin/addons/base/ir/ir_rule.py +++ b/bin/addons/base/ir/ir_rule.py @@ -105,18 +105,17 @@ class ir_rule(osv.osv): def _domain_force_get(self, cr, uid, ids, field_name, arg, context={}): res = {} for rule in self.browse(cr, uid, ids, context): + eval_user_data = {'user': self.pool.get('res.users').browse(cr, 1, uid), + 'time':time} if rule.domain_force: - res[rule.id] = eval(rule.domain_force, {'user': self.pool.get('res.users').browse(cr, 1, uid), - 'time':time}) + res[rule.id] = eval(rule.domain_force, eval_user_data) else: if rule.operator in ('in', 'child_of'): dom = eval("[('%s', '%s', [%s])]" % (rule.field_id.name, rule.operator, - rule.operand), {'user': self.pool.get('res.users').browse(cr, 1, uid), - 'time':time}) + eval(rule.operand,eval_user_data)), eval_user_data) else: dom = eval("[('%s', '%s', %s)]" % (rule.field_id.name, rule.operator, - rule.operand), {'user': self.pool.get('res.users').browse(cr, 1, uid), - 'time':time}) + rule.operand), eval_user_data) res[rule.id] = dom return res diff --git a/bin/addons/base/ir/ir_ui_view.py b/bin/addons/base/ir/ir_ui_view.py index f80b865db1b..b1e15b34cf4 100644 --- a/bin/addons/base/ir/ir_ui_view.py +++ b/bin/addons/base/ir/ir_ui_view.py @@ -57,6 +57,7 @@ class view(osv.osv): 'type': fields.selection(( ('tree','Tree'), ('form','Form'), + ('mdx','mdx'), ('graph', 'Graph'), ('calendar', 'Calendar'), ('gantt', 'Gantt')), 'View Type', required=True), diff --git a/bin/addons/base/ir/ir_values.py b/bin/addons/base/ir/ir_values.py index 5b64622f356..7965c88790a 100644 --- a/bin/addons/base/ir/ir_values.py +++ b/bin/addons/base/ir/ir_values.py @@ -21,6 +21,7 @@ ############################################################################## from osv import osv,fields +from osv.orm import except_orm import pickle from tools.translate import _ @@ -225,7 +226,10 @@ class ir_values(osv.osv): del fields[pos] else: pos+=1 - datas = self.pool.get(model).read(cr, uid, [id], fields, context) + try: + datas = self.pool.get(model).read(cr, uid, [id], fields, context) + except except_orm: + return False datas= datas and datas[0] or None if not datas: #ir_del(cr, uid, x[0]) diff --git a/bin/addons/base/module/module.py b/bin/addons/base/module/module.py index e5e764184c5..7867afa7b92 100644 --- a/bin/addons/base/module/module.py +++ b/bin/addons/base/module/module.py @@ -398,12 +398,12 @@ class module(osv.osv): if not download: continue zipfile = urllib.urlopen(mod.url).read() - fname = addons.get_module_path(mod.name+'.zip') + fname = addons.get_module_path(str(mod.name)+'.zip', downloaded=True) try: fp = file(fname, 'wb') fp.write(zipfile) fp.close() - except IOError, e: + except Exception, e: raise orm.except_orm(_('Error'), _('Can not create the module file:\n %s') % (fname,)) terp = self.get_module_info(mod.name) self.write(cr, uid, mod.id, { diff --git a/bin/addons/base/report/corporate_defaults.xsl b/bin/addons/base/report/corporate_defaults.xsl new file mode 100644 index 00000000000..de2ef8ee2e1 --- /dev/null +++ b/bin/addons/base/report/corporate_defaults.xsl @@ -0,0 +1,96 @@ + + + + + Open ERP Report + 1cm + 1cm + 1cm + 1cm + 21cm,29.7cm + + a4_letter + + + + + + + + 1cm 25.4cm 20cm 25.4cm + + + + + + + + + + - + + + Phone: + + Mail: + + + + + + 1.5cm 1.2cm 19.9cm 1.2cm + + + Your contact : + + + + + + + + + + + 1cm 25.4cm 20cm 25.4cm + + + + + + + + + + - + + + Phone: + + Mail: + + + + + 1.5cm 1.2cm 19.9cm 1.2cm + + + + + + + + + + + + + + + + + + + + + diff --git a/bin/addons/base/res/partner/partner.py b/bin/addons/base/res/partner/partner.py index cfe1cb41710..97c2ed45ac3 100644 --- a/bin/addons/base/res/partner/partner.py +++ b/bin/addons/base/res/partner/partner.py @@ -163,10 +163,6 @@ class res_partner(osv.osv): 'customer': lambda *a: 1, 'category_id': _default_category, } - _sql_constraints = [ - ('name_uniq', 'unique (name)', 'The name of the partner must be unique !') - ] - def copy(self, cr, uid, id, default=None, context={}): name = self.read(cr, uid, [id], ['name'])[0]['name'] default.update({'name': name+' (copy)'}) diff --git a/bin/addons/base/res/partner/wizard/wizard_spam.py b/bin/addons/base/res/partner/wizard/wizard_spam.py index 435bc2a9e94..aa10b08c73e 100644 --- a/bin/addons/base/res/partner/wizard/wizard_spam.py +++ b/bin/addons/base/res/partner/wizard/wizard_spam.py @@ -30,7 +30,7 @@ email_send_form = ''' - + ''' email_send_fields = { diff --git a/bin/addons/base/rng/view.rng b/bin/addons/base/rng/view.rng index d9318adbf10..56f4b097db8 100644 --- a/bin/addons/base/rng/view.rng +++ b/bin/addons/base/rng/view.rng @@ -191,15 +191,7 @@ - - - - - - - - - + diff --git a/bin/import_xml.rng b/bin/import_xml.rng index 3f31b109293..7fe7953ae05 100644 --- a/bin/import_xml.rng +++ b/bin/import_xml.rng @@ -143,6 +143,14 @@ + + + + + + + + @@ -196,6 +204,7 @@ + diff --git a/bin/netsvc.py b/bin/netsvc.py index 4056a10b871..07a6e9c6b71 100644 --- a/bin/netsvc.py +++ b/bin/netsvc.py @@ -182,29 +182,33 @@ import tools init_logger() class Agent(object): - _timers = [] + _timers = {} _logger = Logger() - def setAlarm(self, fn, dt, args=None, kwargs=None): - if not args: - args = [] - if not kwargs: - kwargs = {} + def setAlarm(self, fn, dt, db_name, *args, **kwargs): wait = dt - time.time() if wait > 0: self._logger.notifyChannel('timers', LOG_DEBUG, "Job scheduled in %s seconds for %s.%s" % (wait, fn.im_class.__name__, fn.func_name)) timer = threading.Timer(wait, fn, args, kwargs) timer.start() - self._timers.append(timer) - for timer in self._timers[:]: - if not timer.isAlive(): - self._timers.remove(timer) + self._timers.setdefault(db_name, []).append(timer) + for db in self._timers: + for timer in self._timers[db]: + if not timer.isAlive(): + self._timers[db].remove(timer) + + @classmethod + def cancel(cls, db_name): + """Cancel all timers for a given database. If None passed, all timers are cancelled""" + for db in cls._timers: + if db_name is None or db == db_name: + for timer in cls._timers[db]: + timer.cancel() + + @classmethod def quit(cls): - for timer in cls._timers: - timer.cancel() - quit = classmethod(quit) - + cls.cancel(None) import traceback diff --git a/bin/osv/expression.py b/bin/osv/expression.py index 24e2c9a8d33..2ee1492ddd0 100644 --- a/bin/osv/expression.py +++ b/bin/osv/expression.py @@ -72,7 +72,7 @@ class expression(object): if not self.__exp: return self - def _rec_get(ids, table, parent, left='id', prefix=''): + def _rec_get(ids, table, parent=None, left='id', prefix=''): if table._parent_store and (not table.pool._init): # TODO: Improve where joins are implemented for many with '.', replace by: # doms += ['&',(prefix+'.parent_left','<',o.parent_right),(prefix+'.parent_left','>=',o.parent_left)] @@ -90,7 +90,7 @@ class expression(object): return [] ids2 = table.search(cr, uid, [(parent, 'in', ids)], context=context) return ids + rg(ids2, table, parent) - return [(left, 'in', rg(ids, table, parent))] + return [(left, 'in', rg(ids, table, parent or table._parent_name))] self.__main_table = table @@ -114,7 +114,7 @@ class expression(object): field = working_table._columns.get(fargs[0], False) if not field: if left == 'id' and operator == 'child_of': - dom = _rec_get(right, working_table, working_table._parent_name) + dom = _rec_get(right, working_table) self.__exp = self.__exp[:i] + dom + self.__exp[i+1:] continue @@ -167,7 +167,7 @@ class expression(object): return ids return self.__execute_recursive_in(cr, field._id1, field._rel, field._id2, ids) - dom = _rec_get(ids2, field_obj, working_table._parent_name) + dom = _rec_get(ids2, field_obj) ids2 = field_obj.search(cr, uid, dom, context=context) self.__exp[i] = ('id', 'in', _rec_convert(ids2)) else: @@ -185,9 +185,9 @@ class expression(object): self.__operator = 'in' if field._obj != working_table._name: - dom = _rec_get(ids2, field_obj, working_table._parent_name, left=left, prefix=field._obj) + dom = _rec_get(ids2, field_obj, left=left, prefix=field._obj) else: - dom = _rec_get(ids2, working_table, left) + dom = _rec_get(ids2, working_table, parent=left) self.__exp = self.__exp[:i] + dom + self.__exp[i+1:] else: if isinstance(right, basestring): # and not isinstance(field, fields.related): diff --git a/bin/osv/fields.py b/bin/osv/fields.py index b44579957c4..bfd1adeef9e 100644 --- a/bin/osv/fields.py +++ b/bin/osv/fields.py @@ -308,11 +308,9 @@ class many2one(_column): names = dict(obj.name_get(cr, user, filter(None, res.values()), context)) except except_orm: names = {} - iids = filter(None, res.values()) - cr.execute('select id,'+obj._rec_name+' from '+obj._table+' where id in ('+','.join(map(str, iids))+')') - for res22 in cr.fetchall(): - names[res22[0]] = res22[1] + for iiid in iids: + names[iiid] = '// Access Denied //' for r in res.keys(): if res[r] and res[r] in names: @@ -626,7 +624,7 @@ class function(_column): if self._type == 'binary' and context.get('bin_size', False): # convert the data returned by the function with the size of that data... - res = dict(map(lambda (x, y): (x, tools.human_size(len(y))), res.items())) + res = dict(map(lambda (x, y): (x, tools.human_size(len(y or ''))), res.items())) return res def set(self, cr, obj, id, name, value, user=None, context=None): diff --git a/bin/osv/orm.py b/bin/osv/orm.py index 4431937b924..acb7c0eee84 100644 --- a/bin/osv/orm.py +++ b/bin/osv/orm.py @@ -61,7 +61,7 @@ except ImportError: from tools.config import config -regex_order = re.compile('^([a-zA-Z0-9_]+( desc)?( asc)?,?)+$', re.I) +regex_order = re.compile('^([a-z0-9_]+( *desc| *asc)?( *, *|))+$', re.I) def last_day_of_current_month(): import datetime @@ -101,7 +101,7 @@ class browse_null(object): def __nonzero__(self): return False - + def __unicode__(self): return u'' @@ -323,7 +323,7 @@ class orm_template(object): _inherits = {} _table = None _invalids = set() - + CONCURRENCY_CHECK_FIELD = '__last_update' def _field_create(self, cr, context={}): @@ -474,7 +474,7 @@ class orm_template(object): datas += self.__export_row(cr, uid, row, fields, context) return datas - def import_data(self, cr, uid, fields, datas, mode='init', current_module=None, noupdate=False, context=None, filename=None): + def import_data(self, cr, uid, fields, datas, mode='init', current_module='', noupdate=False, context=None, filename=None): if not context: context = {} fields = map(lambda x: x.split('/'), fields) @@ -735,7 +735,7 @@ class orm_template(object): model_access_obj = self.pool.get('ir.model.access') for parent in self._inherits: res.update(self.pool.get(parent).fields_get(cr, user, fields, context)) - + if self._columns.keys(): for f in self._columns.keys(): if fields and f not in fields: @@ -752,14 +752,14 @@ class orm_template(object): if hasattr(self._columns[f], arg) \ and getattr(self._columns[f], arg): res[f][arg] = getattr(self._columns[f], arg) - + res_trans = translation_obj._get_source(cr, user, self._name + ',' + f, 'field', context.get('lang', False) or 'en_US') if res_trans: res[f]['string'] = res_trans help_trans = translation_obj._get_source(cr, user, self._name + ',' + f, 'help', context.get('lang', False) or 'en_US') if help_trans: res[f]['help'] = help_trans - + if hasattr(self._columns[f], 'selection'): if isinstance(self._columns[f].selection, (tuple, list)): sel = self._columns[f].selection @@ -781,9 +781,9 @@ class orm_template(object): res[f]['domain'] = self._columns[f]._domain res[f]['context'] = self._columns[f]._context else: - #TODO : read the fields from the database + #TODO : read the fields from the database pass - + if fields: # filter out fields which aren't in the fields list for r in res.keys(): @@ -883,7 +883,7 @@ class orm_template(object): rolesobj = self.pool.get('res.roles') usersobj = self.pool.get('res.users') - buttons = xpath.Evaluate("//button[@type != 'object']", node) + buttons = (n for n in node.getElementsByTagName('button') if n.getAttribute('type') != 'object') for button in buttons: ok = True if user != 1: # admin user has all roles @@ -953,11 +953,11 @@ class orm_template(object): def fields_view_get(self, cr, user, view_id=None, view_type='form', context=None, toolbar=False): if not context: context = {} - + def encode(s): if isinstance(s, unicode): return s.encode('utf8') - return s + return s def _inherit_apply(src, inherit): def _find(node, node2): @@ -981,7 +981,7 @@ class orm_template(object): if res: return res return None - + doc_src = dom.minidom.parseString(encode(src)) doc_dest = dom.minidom.parseString(encode(inherit)) @@ -1390,7 +1390,7 @@ class orm_memory(orm_template): 'id': id }) return result - + def _check_removed_columns(self, cr, log=False): # nothing to check in memory... pass @@ -1458,7 +1458,7 @@ class orm(orm_template): " AND c.oid=a.attrelid" " AND a.attisdropped=%%s" " AND pg_catalog.format_type(a.atttypid, a.atttypmod) NOT IN ('cid', 'tid', 'oid', 'xid')" - " AND a.attname NOT IN (%s)" % ",".join(['%s']*len(columns)), + " AND a.attname NOT IN (%s)" % ",".join(['%s']*len(columns)), [self._table, False] + columns) for column in cr.dictfetchall(): if log: @@ -1511,7 +1511,7 @@ class orm(orm_template): if not cr.rowcount: cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (self._table, k, logs[k])) cr.commit() - + self._check_removed_columns(cr, log=False) # iterate on the "object columns" @@ -1665,20 +1665,20 @@ class orm(orm_template): if isinstance(f, fields.many2one): ref = self.pool.get(f._obj)._table if ref != 'ir_actions': - cr.execute('SELECT confdeltype, conname FROM pg_constraint as con, pg_class as cl1, pg_class as cl2, ' - 'pg_attribute as att1, pg_attribute as att2 ' - 'WHERE con.conrelid = cl1.oid ' - 'AND cl1.relname = %s ' - 'AND con.confrelid = cl2.oid ' - 'AND cl2.relname = %s ' - 'AND array_lower(con.conkey, 1) = 1 ' - 'AND con.conkey[1] = att1.attnum ' - 'AND att1.attrelid = cl1.oid ' - 'AND att1.attname = %s ' - 'AND array_lower(con.confkey, 1) = 1 ' - 'AND con.confkey[1] = att2.attnum ' - 'AND att2.attrelid = cl2.oid ' - 'AND att2.attname = %s ' + cr.execute('SELECT confdeltype, conname FROM pg_constraint as con, pg_class as cl1, pg_class as cl2, ' + 'pg_attribute as att1, pg_attribute as att2 ' + 'WHERE con.conrelid = cl1.oid ' + 'AND cl1.relname = %s ' + 'AND con.confrelid = cl2.oid ' + 'AND cl2.relname = %s ' + 'AND array_lower(con.conkey, 1) = 1 ' + 'AND con.conkey[1] = att1.attnum ' + 'AND att1.attrelid = cl1.oid ' + 'AND att1.attname = %s ' + 'AND array_lower(con.confkey, 1) = 1 ' + 'AND con.confkey[1] = att2.attnum ' + 'AND att2.attrelid = cl2.oid ' + 'AND att2.attname = %s ' "AND con.contype = 'f'", (self._table, ref, k, 'id')) res = cr.dictfetchall() if res: @@ -1725,7 +1725,7 @@ class orm(orm_template): def __init__(self, cr): super(orm, self).__init__(cr) - + if not hasattr(self, '_log_access'): # if not access is not specify, it is the same value as _auto self._log_access = not hasattr(self, "_auto") or self._auto @@ -1790,7 +1790,7 @@ class orm(orm_template): import random _rel1 = field['relation'].replace('.', '_') _rel2 = field['model'].replace('.', '_') - _rel_name = 'x_%s_%s_%s_rel' %(_rel1, _rel2, random.randint(0, 10000)) + _rel_name = 'x_%s_%s_%s_rel' %(_rel1, _rel2, field['name']) self._columns[field['name']] = getattr(fields, field['ttype'])(field['relation'], _rel_name, 'id1', 'id2', **attrs) else: self._columns[field['name']] = getattr(fields, field['ttype'])(**attrs) @@ -1925,7 +1925,7 @@ class orm(orm_template): # all inherited fields + all non inherited fields for which the attribute whose name is in load is True fields_pre = [f for f in fields_to_read if - f == self.CONCURRENCY_CHECK_FIELD + f == self.CONCURRENCY_CHECK_FIELD or (f in self._columns and getattr(self._columns[f], '_classic_write')) ] + self._inherits.values() @@ -2092,8 +2092,8 @@ class orm(orm_template): return "%s,%s" % (self._name, oid) santa = "(id = %s AND %s < COALESCE(write_date, create_date, now())::timestamp)" for i in range(0, len(ids), cr.IN_MAX): - sub_ids = tools.flatten(((oid, context[self.CONCURRENCY_CHECK_FIELD][key(oid)]) - for oid in ids[i:i+cr.IN_MAX] + sub_ids = tools.flatten(((oid, context[self.CONCURRENCY_CHECK_FIELD][key(oid)]) + for oid in ids[i:i+cr.IN_MAX] if key(oid) in context[self.CONCURRENCY_CHECK_FIELD])) if sub_ids: cr.execute("SELECT count(1) FROM %s WHERE %s" % (self._table, " OR ".join([santa]*(len(sub_ids)/2))), sub_ids) @@ -2108,7 +2108,7 @@ class orm(orm_template): ids = [ids] result_store = self._store_get_values(cr, uid, ids, None, context) - + self._check_concurrency(cr, ids, context) self.pool.get('ir.model.access').check(cr, uid, self._name, 'unlink') @@ -2147,10 +2147,11 @@ class orm(orm_template): for order, object, ids, fields in result_store: if object<>self._name: - cr.execute('select id from '+self._table+' where id in ('+','.join(map(str, ids))+')') + obj = self.pool.get(object) + cr.execute('select id from '+obj._table+' where id in ('+','.join(map(str, ids))+')') ids = map(lambda x: x[0], cr.fetchall()) if ids: - self.pool.get(object)._store_set_values(cr, uid, ids, fields, context) + obj._store_set_values(cr, uid, ids, fields, context) return True # @@ -2302,60 +2303,42 @@ class orm(orm_template): self.pool._init_parent[self._name]=True else: for id in ids: + # Find Position of the element if vals[self._parent_name]: cr.execute('select parent_left,parent_right,id from '+self._table+' where '+self._parent_name+'=%s order by '+(self._parent_order or self._order), (vals[self._parent_name],)) - pleft_old = pright_old = None - result_p = cr.fetchall() - for (pleft,pright,pid) in result_p: - if pid == id: - break - pleft_old = pleft - pright_old = pright - if not pleft_old: - cr.execute('select parent_left,parent_right from '+self._table+' where id=%s', (vals[self._parent_name],)) - pleft_old,pright_old = cr.fetchone() - res = (pleft_old, pright_old) else: - cr.execute('SELECT parent_left,parent_right FROM '+self._table+' WHERE id IS NULL') - res = cr.fetchone() - if res: - pleft,pright = res - else: - cr.execute('select max(parent_right),max(parent_right)+1 from '+self._table) - pleft,pright = cr.fetchone() - cr.execute('select parent_left,parent_right,id from '+self._table+' where id in ('+','.join(map(lambda x:'%s',ids))+')', ids) - dest = pleft + 1 - for cleft,cright,cid in cr.fetchall(): - if cleft > pleft: - treeshift = pleft - cleft + 1 - leftbound = pleft+1 - rightbound = cleft-1 - cwidth = cright-cleft+1 - leftrange = cright - rightrange = pleft + cr.execute('select parent_left,parent_right,id from '+self._table+' where '+self._parent_name+' is null order by '+(self._parent_order or self._order)) + result_p = cr.fetchall() + position = None + for (pleft,pright,pid) in result_p: + if pid == id: + break + position = pright+1 + + # It's the first node of the parent: position = parent_left+1 + if not position: + if not vals[self._parent_name]: + position = 1 else: - treeshift = pleft - cright - leftbound = cright + 1 - rightbound = pleft - cwidth = cleft-cright-1 - leftrange = pleft+1 - rightrange = cleft - cr.execute('UPDATE '+self._table+''' - SET - parent_left = CASE - WHEN parent_left BETWEEN %s AND %s THEN parent_left + %s - WHEN parent_left BETWEEN %s AND %s THEN parent_left + %s - ELSE parent_left - END, - parent_right = CASE - WHEN parent_right BETWEEN %s AND %s THEN parent_right + %s - WHEN parent_right BETWEEN %s AND %s THEN parent_right + %s - ELSE parent_right - END - WHERE - parent_left<%s OR parent_right>%s; - ''', (leftbound,rightbound,cwidth,cleft,cright,treeshift,leftbound,rightbound, - cwidth,cleft,cright,treeshift,leftrange,rightrange)) + cr.execute('select parent_left from '+self._table+' where id=%s', (vals[self._parent_name],)) + position = cr.fetchone()[0]+1 + + # We have the new position ! + cr.execute('select parent_left,parent_right from '+self._table+' where id=%s', (id,)) + pleft,pright = cr.fetchone() + distance = pright - pleft + 1 + + if position>pleft and position<=pright: + raise except_orm(_('UserError'), _('Recursivity Detected.')) + + if pleft=%s', (distance, position)) + cr.execute('update '+self._table+' set parent_right=parent_right+%s where parent_right>=%s', (distance, position)) + cr.execute('update '+self._table+' set parent_left=parent_left+%s, parent_right=parent_right+%s where parent_left>=%s and parent_left<%s', (position-pleft,position-pleft, pleft, pright)) + else: + cr.execute('update '+self._table+' set parent_left=parent_left+%s where parent_left>=%s', (distance, position)) + cr.execute('update '+self._table+' set parent_right=parent_right+%s where parent_right>=%s', (distance, position)) + cr.execute('update '+self._table+' set parent_left=parent_left-%s, parent_right=parent_right-%s where parent_left>=%s and parent_left<%s', (pleft-position+distance,pleft-position+distance, pleft+distance, pright+distance)) result = self._store_get_values(cr, user, ids, vals.keys(), context) for order, object, ids, fields in result: @@ -2418,12 +2401,12 @@ class orm(orm_template): # Try-except added to filter the creation of those records whose filds are readonly. # Example : any dashboard which has all the fields readonly.(due to Views(database views)) - try: + try: cr.execute("SELECT nextval('"+self._sequence+"')") except: raise except_orm(_('UserError'), - _('You cannot perform this operation.')) - + _('You cannot perform this operation.')) + id_new = cr.fetchone()[0] for table in tocreate: id = self.pool.get(table).create(cr, user, tocreate[table]) @@ -2508,15 +2491,20 @@ class orm(orm_template): for id in filter(None, ids2): result[fncts[fnct][0]].setdefault(id, []) result[fncts[fnct][0]][id].append(fnct) - result2 = [] + dict = {} for object in result: k2 = {} for id,fnct in result[object].items(): k2.setdefault(tuple(fnct), []) k2[tuple(fnct)].append(id) for fnct,id in k2.items(): - result2.append((fncts[fnct[0]][4],object,id,map(lambda x: fncts[x][1], fnct))) - result2.sort() + dict.setdefault(fncts[fnct[0]][4],[]) + dict[fncts[fnct[0]][4]].append((fncts[fnct[0]][4],object,id,map(lambda x: fncts[x][1], fnct))) + result2 = [] + tmp = dict.keys() + tmp.sort() + for k in tmp: + result2+=dict[k] return result2 def _store_set_values(self, cr, uid, ids, fields, context): @@ -2737,6 +2725,7 @@ class orm(orm_template): return data, trans_data def copy(self, cr, uid, id, default=None, context=None): + trans_obj = self.pool.get('ir.translation') data, trans_data = self.copy_data(cr, uid, id, default, context) new_id=self.create(cr, uid, data) for record in trans_data: diff --git a/bin/pooler.py b/bin/pooler.py index 0064e2397b6..32b45dbd170 100644 --- a/bin/pooler.py +++ b/bin/pooler.py @@ -57,6 +57,7 @@ def get_db_and_pool(db_name, force_demo=False, status=None, update_module=False) def restart_pool(db_name, force_demo=False, status=None, update_module=False): if db_name in pool_dic: + pool_dic[db_name].get('ir.cron').cancel(db_name) del pool_dic[db_name] return get_db_and_pool(db_name, force_demo, status, update_module=update_module) diff --git a/bin/report/render/rml2html/rml2html.py b/bin/report/render/rml2html/rml2html.py index 5d005cc45a6..1033c64d454 100644 --- a/bin/report/render/rml2html/rml2html.py +++ b/bin/report/render/rml2html/rml2html.py @@ -1,7 +1,7 @@ # -*- encoding: utf-8 -*- ############################################################################## # -# OpenERP, Open Source Management Solution +# OpenERP, Open Source Management Solution # Copyright (C) 2004-2009 Tiny SPRL (). All Rights Reserved # $Id$ # @@ -94,7 +94,6 @@ class _flowable(object): if node.hasAttribute('style'): node.setAttribute('class', node.getAttribute('style')) return node.toxml() - def render(self, node): result = self.template.start() result += self.template.frame_start() @@ -107,8 +106,7 @@ class _flowable(object): #print 'tag', n.localName, 'not yet implemented!' result += self.template.frame_stop() result += self.template.end() - return result - + return result.encode('utf-8').replace('"',"\'").replace('°','°') class _rml_tmpl_tag(object): def __init__(self, *args): pass @@ -126,7 +124,7 @@ class _rml_tmpl_frame(_rml_tmpl_tag): self.width = width self.posx = posx def tag_start(self): - return '
 ' % (self.width+self.posx,self.posx) + return "
 " % (self.width+self.posx,self.posx) def tag_end(self): return True def tag_stop(self): @@ -152,19 +150,19 @@ class _rml_tmpl_draw_string(_rml_tmpl_tag): def tag_start(self): self.pos.sort() - res = '' + res = "
" posx = 0 i = 0 for (x,y,align,txt, style, fs) in self.pos: if align=="left": pos2 = len(txt)*fs - res+='' % (x - posx, style, pos2, txt) + res+="" % (x - posx, style, pos2, txt) posx = x+pos2 if align=="right": - res+='' % (x - posx, style, txt) + res+="" % (x - posx, style, txt) posx = x if align=="center": - res+='' % ((x - posx)*2, style, txt) + res+="" % ((x - posx)*2, style, txt) posx = 2*x-posx i+=1 res+='
%s%s%s%s%s%s
' @@ -185,7 +183,7 @@ class _rml_tmpl_draw_lines(_rml_tmpl_tag): def tag_start(self): if self.ok: - return '

' % (self.posx+self.width,self.posx,self.style) + return "

" % (self.posx+self.width,self.posx,self.style) else: return '' @@ -207,9 +205,9 @@ class _rml_stylesheet(object): attrs = [] for a in attr: if a in self._tags: - attrs.append("%s:%s" % self._tags[a](attr[a])) + attrs.append('%s:%s' % self._tags[a](attr[a])) if len(attrs): - result += "p."+attr['name']+" {"+'; '.join(attrs)+"}\n" + result += 'p.'+attr['name']+' {'+'; '.join(attrs)+'}\n' self.result = result def render(self): @@ -338,6 +336,7 @@ class _rml_doc(object): self.result += ''' + - -''' +''' - template = _rml_template(self.dom.documentElement.getElementsByTagName('template')[0]) - f = _flowable(template, self.dom) - self.result += f.render(self.dom.documentElement.getElementsByTagName('story')[0]) + +# f = _flowable(template, self.dom) + list_story =[] + storys= self.dom.documentElement.getElementsByTagName('story') + for story in storys : + template = _rml_template(self.dom.documentElement.getElementsByTagName('template')[0]) + f = _flowable(template, self.dom) + story_text = f.render(story) + list_story.append(story_text) del f - self.result += '' + self.result +=''' + + + +
+ %s +
+
+ + + + '''%(list_story,list_story[0]) out.write( self.result) def parseString(data, fout=None): @@ -383,5 +418,4 @@ if __name__=="__main__": print 'Usage: trml2pdf input.rml >output.pdf' print 'Try \'trml2pdf --help\' for more information.' -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: - +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: \ No newline at end of file diff --git a/bin/report/report_sxw.py b/bin/report/report_sxw.py index 8e0537cf933..fd8d5e35c09 100644 --- a/bin/report/report_sxw.py +++ b/bin/report/report_sxw.py @@ -519,8 +519,8 @@ class rml_parse(object): if not self.logo and rml_head.find('company.logo')>=0: rml_head = rml_head.replace('','') - - head_dom = xml.dom.minidom.parseString(rml_head) + + head_dom = xml.dom.minidom.parseString(rml_head.encode('utf-8')) #for frame in head_dom.getElementsByTagName('frame'): # frame.parentNode.removeChild(frame) node2 = head_dom.documentElement diff --git a/bin/service/security.py b/bin/service/security.py index 449740a99ff..040d71be6e1 100644 --- a/bin/service/security.py +++ b/bin/service/security.py @@ -40,11 +40,10 @@ def check_super(passwd): return True else: raise Exception('AccessDenied') - + def check(db, uid, passwd): if _uid_cache.get(db, {}).get(uid) == passwd: return True - cr = pooler.get_db(db).cursor() cr.execute('select count(*) from res_users where id=%s and password=%s', (int(uid), passwd)) res = cr.fetchone()[0] diff --git a/bin/tools/config.py b/bin/tools/config.py index f3329d5d6eb..178211f0635 100644 --- a/bin/tools/config.py +++ b/bin/tools/config.py @@ -82,7 +82,7 @@ class configmanager(object): hasSSL = check_ssl() - loglevels = dict([(getattr(netsvc, 'LOG_%s' % x), getattr(logging, x)) + self._LOGLEVELS = dict([(getattr(netsvc, 'LOG_%s' % x), getattr(logging, x)) for x in ('CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG', 'DEBUG_RPC', 'NOTSET')]) version = "%s %s" % (release.description, release.version) @@ -111,8 +111,8 @@ class configmanager(object): parser.add_option("--stop-after-init", action="store_true", dest="stop_after_init", default=False, help="stop the server after it initializes") parser.add_option('--debug', dest='debug_mode', action='store_true', default=False, help='enable debug mode') - parser.add_option("--assert-exit-level", dest='assert_exit_level', type="choice", choices=loglevels.keys(), - help="specify the level at which a failed assertion will stop the server. Accepted values: %s" % (loglevels.keys(),)) + parser.add_option("--assert-exit-level", dest='assert_exit_level', type="choice", choices=self._LOGLEVELS.keys(), + help="specify the level at which a failed assertion will stop the server. Accepted values: %s" % (self._LOGLEVELS.keys(),)) if hasSSL: group = optparse.OptionGroup(parser, "SSL Configuration") group.add_option("-S", "--secure", dest="secure", action="store_true", @@ -130,8 +130,8 @@ class configmanager(object): group.add_option("--logfile", dest="logfile", help="file where the server log will be stored") group.add_option("--syslog", action="store_true", dest="syslog", default=False, help="Send the log to the syslog server") - group.add_option('--log-level', dest='log_level', type='choice', choices=loglevels.keys(), - help='specify the level of the logging. Accepted values: ' + str(loglevels.keys())) + group.add_option('--log-level', dest='log_level', type='choice', choices=self._LOGLEVELS.keys(), + help='specify the level of the logging. Accepted values: ' + str(self._LOGLEVELS.keys())) parser.add_option_group(group) # SMTP Group @@ -235,10 +235,14 @@ class configmanager(object): self.options[arg] = getattr(opt, arg) if opt.assert_exit_level: - self.options['assert_exit_level'] = loglevels[opt.assert_exit_level] + self.options['assert_exit_level'] = self._LOGLEVELS[opt.assert_exit_level] + else: + self.options['assert_exit_level'] = self._LOGLEVELS.get(self.options['assert_exit_level']) or int(self.options['assert_exit_level']) if opt.log_level: - self.options['log_level'] = loglevels[opt.log_level] + self.options['log_level'] = self._LOGLEVELS[opt.log_level] + else: + self.options['log_level'] = self._LOGLEVELS.get(self.options['log_level']) or int(self.options['log_level']) if not self.options['root_path'] or self.options['root_path']=='None': self.options['root_path'] = os.path.abspath(os.path.dirname(sys.argv[0])) @@ -335,9 +339,15 @@ class configmanager(object): def save(self): p = ConfigParser.ConfigParser() + loglevelnames = dict(zip(self._LOGLEVELS.values(), self._LOGLEVELS.keys())) p.add_section('options') - for o in [opt for opt in self.options.keys() if opt not in ('version','language','translate_out','translate_in','init','update')]: - p.set('options', o, self.options[o]) + for opt in self.options.keys(): + if opt in ('version', 'language', 'translate_out', 'translate_in', 'init', 'update'): + continue + if opt in ('log_level', 'assert_exit_level'): + p.set('options', opt, loglevelnames.get(self.options[opt], self.options[opt])) + else: + p.set('options', opt, self.options[opt]) # try to create the directories and write the file try: @@ -345,6 +355,7 @@ class configmanager(object): os.makedirs(os.path.dirname(self.rcfile)) try: p.write(file(self.rcfile, 'w')) + os.chmod(self.rcfile, 0600) except IOError: sys.stderr.write("ERROR: couldn't write the config file\n") diff --git a/bin/tools/misc.py b/bin/tools/misc.py index cba90e61d6b..997a68d3eb0 100644 --- a/bin/tools/misc.py +++ b/bin/tools/misc.py @@ -302,7 +302,8 @@ def reverse_enumerate(l): #---------------------------------------------------------- # Emails #---------------------------------------------------------- -def email_send(email_from, email_to, subject, body, email_cc=None, email_bcc=None, reply_to=False, attach=None, tinycrm=False, ssl=False, debug=False, subtype='plain'): +def email_send(email_from, email_to, subject, body, email_cc=None, email_bcc=None, reply_to=False, + attach=None, tinycrm=False, ssl=False, debug=False, subtype='plain', x_headers=None): """Send an email.""" import smtplib from email.MIMEText import MIMEText @@ -313,9 +314,15 @@ def email_send(email_from, email_to, subject, body, email_cc=None, email_bcc=Non from email.Utils import formatdate, COMMASPACE from email import Encoders + if x_headers is None: + x_headers = {} + if not ssl: ssl = config.get('smtp_ssl', False) + if not email_from and not config['email_from']: + raise Exception("No Email sender by default, see config file") + if not email_cc: email_cc = [] if not email_bcc: @@ -326,11 +333,13 @@ def email_send(email_from, email_to, subject, body, email_cc=None, email_bcc=Non else: msg = MIMEMultipart() - msg['Subject'] = Header(subject.decode('utf8'), 'utf-8') + msg['Subject'] = Header(ustr(subject), 'utf-8') msg['From'] = email_from del msg['Reply-To'] if reply_to: - msg['Reply-To'] = msg['From']+', '+reply_to + msg['Reply-To'] = reply_to + else: + msg['Reply-To'] = msg['From'] msg['To'] = COMMASPACE.join(email_to) if email_cc: msg['Cc'] = COMMASPACE.join(email_cc) @@ -338,6 +347,15 @@ def email_send(email_from, email_to, subject, body, email_cc=None, email_bcc=Non msg['Bcc'] = COMMASPACE.join(email_bcc) msg['Date'] = formatdate(localtime=True) + # Add OpenERP Server information + msg['X-Generated-By'] = 'OpenERP (http://www.openerp.com)' + msg['X-OpenERP-Server-Host'] = socket.gethostname() + msg['X-OpenERP-Server-Version'] = release.version + + # Add dynamic X Header + for key, value in x_headers.items(): + msg['X-OpenERP-%s' % key] = str(value) + if tinycrm: msg['Message-Id'] = "<%s-tinycrm-%s@%s>" % (time.time(), tinycrm, socket.gethostname()) @@ -381,9 +399,10 @@ def email_send(email_from, email_to, subject, body, email_cc=None, email_bcc=Non # text must be latin-1 encoded def sms_send(user, password, api_id, text, to): import urllib - params = urllib.urlencode({'user': user, 'password': password, 'api_id': api_id, 'text': text, 'to':to}) - #f = urllib.urlopen("http://api.clickatell.com/http/sendmsg", params) - f = urllib.urlopen("http://196.7.150.220/http/sendmsg", params) + url = "http://api.urlsms.com/SendSMS.aspx" + #url = "http://196.7.150.220/http/sendmsg" + params = urllib.urlencode({'UserID': user, 'Password': password, 'SenderID': api_id, 'MsgText': text, 'RecipientMobileNo':to}) + f = urllib.urlopen(url+"?"+params) # FIXME: Use the logger if there is an error return True