diff --git a/CHANGELOG b/CHANGELOG
index b6b74aaf72..f04f3a08f5 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,4 @@
+* Add replace_attributes for xpath tag
* Add email paramter on report action
* Allow to inherit views from an other model
* Add user warnings
diff --git a/doc/views.rst b/doc/views.rst
index 12b47968a1..bf2289fc09 100644
--- a/doc/views.rst
+++ b/doc/views.rst
@@ -430,7 +430,7 @@ xpath
* ``expr``: the xpath expression to find a node in the inherited view.
- * ``position``: Define the position from the finded node, it can be ``before``, ``after``, ``replace``, ``inside``.
+ * ``position``: Define the position from the finded node, it can be ``before``, ``after``, ``replace``, ``inside`` or ``replace_attributes`` which will change the attributes.
Example
+++++++
diff --git a/trytond/ir/action.py b/trytond/ir/action.py
index 125fa65dd6..bcb30bab1c 100644
--- a/trytond/ir/action.py
+++ b/trytond/ir/action.py
@@ -3,6 +3,7 @@
"Action"
from trytond.osv import fields, OSV
from trytond.tools import file_open
+from trytond.sql_db import table_handler
class Action(OSV):
@@ -411,6 +412,15 @@ class ActionActWindowView(OSV):
super(ActionActWindowView, self).__init__()
self._order.insert(0, ('sequence', 'ASC'))
+ def _auto_init(self, cursor, module_name):
+ super(ActionActWindowView, self)._auto_init(cursor, module_name)
+ table = table_handler(cursor, self._table, self._name, module_name)
+
+ # Migration from 1.0 remove multi
+ if 'multi' in table.table:
+ cursor.execute('ALTER TABLE "' + self._table + '" ' \
+ 'DROP COLUMN multi')
+
ActionActWindowView()
diff --git a/trytond/ir/ui/board.rnc b/trytond/ir/ui/board.rnc
index 9e30c02b21..86dc2cb72b 100644
--- a/trytond/ir/ui/board.rnc
+++ b/trytond/ir/ui/board.rnc
@@ -149,12 +149,14 @@ xpath = element xpath { attlist.xpath,
| action
| page
| child1
- | child2)*
+ | child2
+ | board
+ )*
}
attlist.xpath &= attribute expr { text }
attlist.xpath &=
[ a:defaultValue = "inside" ]
- attribute position { "inside" | "replace" | "after" | "before" }?
+ attribute position { "inside" | "replace" | "replace_attributes" | "after" | "before" }?
action |= notAllowed
label |= notAllowed
start = data | board | label
diff --git a/trytond/ir/ui/board.rng b/trytond/ir/ui/board.rng
index e4cf28655d..f66f7bd0c7 100644
--- a/trytond/ir/ui/board.rng
+++ b/trytond/ir/ui/board.rng
@@ -442,6 +442,7 @@
+
@@ -455,6 +456,7 @@
inside
replace
+ replace_attributes
after
before
diff --git a/trytond/ir/ui/form.rnc b/trytond/ir/ui/form.rnc
index d64c3211cf..b11d15346f 100644
--- a/trytond/ir/ui/form.rnc
+++ b/trytond/ir/ui/form.rnc
@@ -272,6 +272,6 @@ xpath = element xpath { attlist.xpath,
attlist.xpath &= attribute expr { text }
attlist.xpath &=
[ a:defaultValue = "inside" ]
- attribute position { "inside" | "replace" | "after" | "before" }?
+ attribute position { "inside" | "replace" | "replace_attributes" | "after" | "before" }?
start = data | form
diff --git a/trytond/ir/ui/form.rng b/trytond/ir/ui/form.rng
index 9c5f00384a..9319a53fb7 100644
--- a/trytond/ir/ui/form.rng
+++ b/trytond/ir/ui/form.rng
@@ -865,6 +865,7 @@
inside
replace
+ replace_attributes
after
before
diff --git a/trytond/ir/ui/graph.rnc b/trytond/ir/ui/graph.rnc
index 526cb6575e..bf0f7f2713 100644
--- a/trytond/ir/ui/graph.rnc
+++ b/trytond/ir/ui/graph.rnc
@@ -34,5 +34,5 @@ xpath = element xpath { attlist.xpath,
attlist.xpath &= attribute expr { text }
attlist.xpath &=
[ a:defaultValue = "inside" ]
- attribute position { "inside" | "replace" | "after" | "before" }?
+ attribute position { "inside" | "replace" | "replace_attributes" | "after" | "before" }?
start = data | graph
diff --git a/trytond/ir/ui/graph.rng b/trytond/ir/ui/graph.rng
index a7b691bbc8..4b1e6070ed 100644
--- a/trytond/ir/ui/graph.rng
+++ b/trytond/ir/ui/graph.rng
@@ -152,6 +152,7 @@
inside
replace
+ replace_attributes
after
before
diff --git a/trytond/ir/ui/tree.rnc b/trytond/ir/ui/tree.rnc
index 1e4a03789b..f5b0969ac8 100644
--- a/trytond/ir/ui/tree.rnc
+++ b/trytond/ir/ui/tree.rnc
@@ -67,10 +67,11 @@ attlist.data &= empty
xpath = element xpath { attlist.xpath,
(field
| button
+ | tree
)*
}
attlist.xpath &= attribute expr { text }
attlist.xpath &=
[ a:defaultValue = "inside" ]
- attribute position { "inside" | "replace" | "after" | "before" }?
+ attribute position { "inside" | "replace" | "replace_attributes" | "after" | "before" }?
start = tree | data
diff --git a/trytond/ir/ui/tree.rng b/trytond/ir/ui/tree.rng
index 70d92618f3..605ea80632 100644
--- a/trytond/ir/ui/tree.rng
+++ b/trytond/ir/ui/tree.rng
@@ -211,6 +211,7 @@
+
@@ -224,6 +225,7 @@
inside
replace
+ replace_attributes
after
before
diff --git a/trytond/osv/orm.py b/trytond/osv/orm.py
index 197a50aede..60a6fa64b1 100644
--- a/trytond/osv/orm.py
+++ b/trytond/osv/orm.py
@@ -2224,6 +2224,10 @@ class ORM(object):
else:
parent.extend(element2.getchildren())
parent.remove(element)
+ elif pos == 'replace_attributes':
+ child = element2.getchildren()[0]
+ for attr in child.attrib:
+ element.set(attr, child.get(attr))
elif pos == 'inside':
element.extend(element2.getchildren())
elif pos == 'after':
diff --git a/trytond/tools/convert.py b/trytond/tools/convert.py
index 191018fd01..ed9262526e 100644
--- a/trytond/tools/convert.py
+++ b/trytond/tools/convert.py
@@ -291,18 +291,28 @@ class Fs2bdAccessor:
Provide some helper function to ease cache access and management.
"""
- def __init__(self, cursor, user, modeldata_obj):
+ def __init__(self, cursor, user, modeldata_obj, pool):
self.fs2db = {}
self.fetched_modules = []
self.modeldata_obj = modeldata_obj
self.cursor = cursor
self.user = user
+ self.browserecord = {}
+ self.pool = pool
def get(self, module, fs_id):
if module not in self.fetched_modules:
self.fetch_new_module(module)
return self.fs2db[module].get(fs_id, None)
+ def get_browserecord(self, module, model_name, db_id):
+ if module not in self.fetched_modules:
+ self.fetch_new_module(module)
+ if model_name in self.browserecord[module] \
+ and db_id in self.browserecord[module][model_name]:
+ return self.browserecord[module][model_name][db_id]
+ return None
+
def set(self, module, fs_id, values):
"""
Whe call the prefetch function here to. Like that whe are sure
@@ -316,6 +326,23 @@ class Fs2bdAccessor:
for key, val in values.items():
fs2db_val[key] = val
+ def reset_browsercord(self, module, model_name, ids=None):
+ if module not in self.fetched_modules:
+ return
+ if model_name not in self.browserecord[module]:
+ return
+ model_obj = self.pool.get(model_name)
+ if not ids:
+ object_ids = self.browserecord[module][model_name].keys()
+ else:
+ if isinstance(ids, (int, long)):
+ object_ids = [ids]
+ else:
+ object_ids = ids
+ models = model_obj.browse(self.cursor, self.user, object_ids)
+ for model in models:
+ self.browserecord[module][model_name][model.id] = model
+
def fetch_new_module(self, module):
if module == "ir.ui.menu": raise
self.fs2db[module] = {}
@@ -324,12 +351,27 @@ class Fs2bdAccessor:
('module', '=', module),
('inherit', '=', False),
])
+
+ record_ids = {}
for rec in self.modeldata_obj.browse(
self.cursor, self.user, module_data_ids):
self.fs2db[rec.module][rec.fs_id] = {
"db_id": rec.db_id, "model": rec.model,
"id": rec.id, "values": rec.values
}
+ record_ids.setdefault(rec.model, [])
+ record_ids[rec.model].append(rec.db_id)
+
+ self.browserecord[module] = {}
+ for model_name in record_ids.keys():
+ model_obj = self.pool.get(model_name)
+ self.browserecord[module][model_name] = {}
+ ids = model_obj.search(self.cursor, self.user, [
+ ('id', 'in', record_ids[model_name]),
+ ], context={'active_test': False})
+ models = model_obj.browse(self.cursor, self.user, ids)
+ for model in models:
+ self.browserecord[module][model_name][model.id] = model
self.fetched_modules.append(module)
@@ -343,7 +385,7 @@ class TrytondXmlHandler(sax.handler.ContentHandler):
self.user = 0
self.module = module
self.modeldata_obj = pool.get('ir.model.data')
- self.fs2db = Fs2bdAccessor(cursor, self.user, self.modeldata_obj)
+ self.fs2db = Fs2bdAccessor(cursor, self.user, self.modeldata_obj, pool)
self.to_delete = self.populate_to_delete()
# Tag handlders are used to delegate the processing
@@ -541,8 +583,7 @@ class TrytondXmlHandler(sax.handler.ContentHandler):
"data with the wrong model: %s (module: %s)" % (fs_id, module))
#Re-create object if it was deleted
- if not object_ref.search(cursor, user, [('id', '=', db_id)],
- context={'active_test': False}):
+ if not self.fs2db.get_browserecord(module, object_ref._name, db_id):
db_id = object_ref.create(cursor, user, values,
context={'module': module})
@@ -563,13 +604,28 @@ class TrytondXmlHandler(sax.handler.ContentHandler):
object_ref._inherit_fields[field_name][0])._name
res_id = inherit_db_ids[table_name]
if field.translate and values.get(field_name):
- cursor.execute('INSERT INTO ir_translation ' \
- '(name, lang, type, src, res_id, ' \
- 'value, module, fuzzy) ' \
- 'VALUES (%s, %s, %s, %s, %s, %s, %s, false)',
+ cursor.execute('SELECT id FROM ir_translation ' \
+ 'WHERE name = %s ' \
+ 'AND lang = %s ' \
+ 'AND type = %s ' \
+ 'AND res_id = %s ' \
+ 'AND module = %s',
(table_name + ',' + field_name,
- 'en_US', 'model', values[field_name],
- res_id, '', module))
+ 'en_US', 'model', res_id, module))
+ if cursor.rowcount:
+ trans_id = cursor.fetchone()[0]
+ cursor.execute('UPDATE ir_translation ' \
+ 'SET src = %s, module = %s ' \
+ 'WHERE id = %s',
+ (values[field_name], module, trans_id))
+ else:
+ cursor.execute('INSERT INTO ir_translation ' \
+ '(name, lang, type, src, res_id, ' \
+ 'value, module, fuzzy) ' \
+ 'VALUES (%s, %s, %s, %s, %s, %s, %s, false)',
+ (table_name + ',' + field_name,
+ 'en_US', 'model', values[field_name],
+ res_id, '', module))
for table in inherit_db_ids.keys():
data_id = self.modeldata_obj.search(cursor, user, [
@@ -602,7 +658,9 @@ class TrytondXmlHandler(sax.handler.ContentHandler):
})
self.fs2db.get(module, fs_id)["db_id"] = db_id
- db_val = object_ref.browse(cursor, user, db_id)
+ db_val = self.fs2db.get_browserecord(module, object_ref._name, db_id)
+ if not db_val:
+ db_val = object_ref.browse(cursor, user, db_id)
to_update = {}
for key in values:
@@ -650,6 +708,7 @@ class TrytondXmlHandler(sax.handler.ContentHandler):
# write the values in the db:
object_ref.write(cursor, user, db_id, to_update,
context={'module': module})
+ self.fs2db.reset_browsercord(module, object_ref._name, db_id)
if not inherit_db_ids:
@@ -667,45 +726,50 @@ class TrytondXmlHandler(sax.handler.ContentHandler):
inherit_mdata_ids.append((table, data_id))
#Update/Create translation record for field translatable
- for field_name in object_ref._columns.keys() + \
- object_ref._inherit_fields.keys():
- if field_name in object_ref._columns:
- field = object_ref._columns[field_name]
- table_name = object_ref._name
- res_id = db_id
- else:
- field = object_ref._inherit_fields[field_name][2]
- table_name = self.pool.get(
- object_ref._inherit_fields[field_name][0])._name
- res_id = inherit_db_ids[table_name]
- if field.translate:
- cursor.execute('SELECT id FROM ir_translation ' \
- 'WHERE name = %s' \
- 'AND lang = %s ' \
- 'AND type = %s ' \
- 'AND res_id = %s',
- (table_name + ',' + field_name,
- 'en_US', 'model', res_id))
- if cursor.rowcount:
- if to_update.get(field_name):
- trans_id = cursor.fetchone()[0]
- cursor.execute('UPDATE ir_translation ' \
- 'SET src = %s, module = %s ' \
- 'WHERE id = %s',
- (to_update[field_name], module, trans_id))
- elif values.get(field_name):
- cursor.execute('INSERT INTO ir_translation ' \
- '(name, lang, type, src, res_id, ' \
- 'value, module, fuzzy) ' \
- 'VALUES (%s, %s, %s, %s, %s, %s, %s, false)',
+ if to_update:
+ for field_name in object_ref._columns.keys() + \
+ object_ref._inherit_fields.keys():
+ if field_name in object_ref._columns:
+ field = object_ref._columns[field_name]
+ table_name = object_ref._name
+ res_id = db_id
+ else:
+ field = object_ref._inherit_fields[field_name][2]
+ table_name = self.pool.get(
+ object_ref._inherit_fields[field_name][0])._name
+ res_id = inherit_db_ids[table_name]
+ if field.translate:
+ cursor.execute('SELECT id FROM ir_translation ' \
+ 'WHERE name = %s ' \
+ 'AND lang = %s ' \
+ 'AND type = %s ' \
+ 'AND res_id = %s ' \
+ 'AND module = %s',
(table_name + ',' + field_name,
- 'en_US', 'model', values[field_name],
- res_id, '', module))
+ 'en_US', 'model', res_id, module))
+ if cursor.rowcount:
+ if to_update.get(field_name):
+ trans_id = cursor.fetchone()[0]
+ cursor.execute('UPDATE ir_translation ' \
+ 'SET src = %s, module = %s ' \
+ 'WHERE id = %s',
+ (to_update[field_name], module, trans_id))
+ elif values.get(field_name):
+ cursor.execute('INSERT INTO ir_translation ' \
+ '(name, lang, type, src, res_id, ' \
+ 'value, module, fuzzy) ' \
+ 'VALUES (%s, %s, %s, %s, %s, %s, %s, false)',
+ (table_name + ',' + field_name,
+ 'en_US', 'model', values[field_name],
+ res_id, '', module))
if to_update:
# re-read it: this ensure that we store the real value
# in the model_data table:
- db_val = object_ref.browse(cursor, user, db_id)
+ db_val = self.fs2db.get_browserecord(module, object_ref._name,
+ db_id)
+ if not db_val:
+ db_val = object_ref.browse(cursor, user, db_id)
for key in to_update:
try:
values[key] = self._clean_value(
diff --git a/trytond/tools/misc.py b/trytond/tools/misc.py
index 69287a5fbf..f0695ac925 100644
--- a/trytond/tools/misc.py
+++ b/trytond/tools/misc.py
@@ -1,4 +1,5 @@
-#This file is part of Tryton. The COPYRIGHT file at the top level of this repository contains the full copyright notices and license terms.
+#This file is part of Tryton. The COPYRIGHT file at the top level of
+#this repository contains the full copyright notices and license terms.
# -*- coding: utf-8 -*-
"""
Miscelleanous tools used by tryton
@@ -27,7 +28,7 @@ def find_in_path(name):
val = os.path.join(directory, name)
if os.path.isfile(val) or os.path.islink(val):
return val
- return None
+ return name
def find_pg_tool(name):
if CONFIG['pg_path'] and CONFIG['pg_path'] != 'None':