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 00a12ee784b..02475e02bd3 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,20 +68,56 @@ 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 - done = Set(self.keys()) - while done: - level_modules = [(name, module) for name, module in self.items() if module.depth==level] - for name, module in level_modules: - done.remove(name) - yield module - level += 1 + done = set() + # first pass: all modules that doesn't have a depend module (or + # themself) in 'to install' state. + first_pass = [] + for name, mod in self.items(): + if all(m.state != 'to install' for m in mod.depends(True)): + first_pass.append(mod) + + first_pass.sort(key=lambda m: m.depth) + for m in first_pass: + done.add(m.name) + yield m + + # second pass: all modules in 'to install' state + second_pass = [m for m in self.values() if m.state == 'to install'] + second_pass.sort(key=lambda m: m.depth) + for m in second_pass: + done.add(m.name) + yield m + + # third pass: all other modules + third_pass = [m for m in self.values() if m.name not in done] + third_pass.sort(key=lambda m: m.depth) + for m in third_pass: + yield m class Singleton(object): - def __new__(cls, name, graph): if name in graph: inst = graph[name] @@ -97,57 +132,70 @@ 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 _depends(self): + """direct depends""" + return list(self.data.get('depends', [])) + + def depends(self, include_self=False): + deps = self._depends() + res = set() + if include_self: + res.add(self) + while deps: + dep = Node(deps.pop(0), self.graph) + res.add(dep) + for d in dep._depends(): + if d not in res: + deps.append(d) + return res 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 @@ -311,7 +359,7 @@ def upgrade_graph(graph, cr, module_list, force=None): packages.append((module, info.get('depends', []), info)) dependencies = dict([(p, deps) for p, deps, data in packages]) - current, later = Set([p for p, dep, data in packages]), Set() + current, later = set([p for p, dep, data in packages]), set() while packages and current > later: package, deps, data = packages[0] @@ -333,6 +381,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))) @@ -551,24 +601,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 @@ -603,9 +635,6 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, **kwargs): 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)) @@ -650,9 +679,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(): @@ -748,7 +775,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 @@ -772,4 +799,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 442147434bd..b75b39f2c9e 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_cron.py b/bin/addons/base/ir/ir_cron.py index 18f853a0d83..8772cd52120 100644 --- a/bin/addons/base/ir/ir_cron.py +++ b/bin/addons/base/ir/ir_cron.py @@ -77,45 +77,45 @@ 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() - finally: - cr.close() + 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() + 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) ir_cron() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: 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_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/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/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 40db5265c92..beb1a2487a4 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 3ad2f2feff0..0dc844b59df 100644 --- a/bin/import_xml.rng +++ b/bin/import_xml.rng @@ -141,6 +141,14 @@ + + + + + + + + @@ -194,6 +202,7 @@ + diff --git a/bin/netsvc.py b/bin/netsvc.py index 75246e981b8..1a806d53766 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/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 6644142517c..719b4cdf857 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 @@ -465,7 +465,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) @@ -872,7 +872,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 @@ -1773,7 +1773,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) @@ -2130,10 +2130,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 # @@ -2285,60 +2286,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: 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 9c653513bb1..fd8d5e35c09 100644 --- a/bin/report/report_sxw.py +++ b/bin/report/report_sxw.py @@ -519,7 +519,7 @@ 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.encode('utf-8')) #for frame in head_dom.getElementsByTagName('frame'): # frame.parentNode.removeChild(frame) 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 a40d0ee3f36..dbb8e270944 100644 --- a/bin/tools/config.py +++ b/bin/tools/config.py @@ -356,6 +356,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 5eee773f0f1..1ead9ea3a92 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