[MERGE]: Merged with lp:openobject-server
bzr revid: atp@tinyerp.com-20120612065235-ndmy1xvphxvub0je bzr revid: atp@tinyerp.com-20120615112550-rkq1qug49ecfm2ig
This commit is contained in:
@@ -73,42 +73,56 @@ class view(osv.osv):
|
||||
}
|
||||
_order = "priority,name"
|
||||
|
||||
def _check_xml(self, cr, uid, ids, context=None):
|
||||
def root_view(record):
|
||||
if not record.inherit_id:
|
||||
return record
|
||||
else:
|
||||
return root_view(record.inherit_id)
|
||||
def call_view(record):
|
||||
try:
|
||||
root = root_view(record)
|
||||
# Temporarily commented, waiting fixes; it breaks most updates because
|
||||
# when you update modules, all views are in the database, but only
|
||||
# fields of loaded objects are on the objects. This breaks the
|
||||
# fields_view_get calls. (field exists in view but not in the object)
|
||||
#
|
||||
# self.pool.get(root.model).fields_view_get(cr, uid, view_id=root.id, view_type=root.type, context=context)
|
||||
# Holds the RNG schema
|
||||
_relaxng_validator = None
|
||||
|
||||
return True
|
||||
except:
|
||||
_logger.exception("Can't obtain view description for model: %s (view id: %s).", record.model, record.id)
|
||||
return False
|
||||
for view in self.browse(cr, uid, ids, context):
|
||||
return call_view(view)
|
||||
|
||||
# TODO The following code should be executed (i.e. no early return above).
|
||||
# TODO The view.rng file can be read only once!
|
||||
eview = etree.fromstring(view.arch.encode('utf8'))
|
||||
def _relaxng(self):
|
||||
if not self._relaxng_validator:
|
||||
frng = tools.file_open(os.path.join('base','rng','view.rng'))
|
||||
try:
|
||||
relaxng_doc = etree.parse(frng)
|
||||
relaxng = etree.RelaxNG(relaxng_doc)
|
||||
if not relaxng.validate(eview):
|
||||
for error in relaxng.error_log:
|
||||
_logger.error(tools.ustr(error))
|
||||
return False
|
||||
self._relaxng_validator = etree.RelaxNG(relaxng_doc)
|
||||
except Exception:
|
||||
_logger.exception('Failed to load RelaxNG XML schema for views validation')
|
||||
finally:
|
||||
frng.close()
|
||||
return self._relaxng_validator
|
||||
|
||||
|
||||
def _check_render_view(self, cr, uid, view, context=None):
|
||||
"""Verify that the given view's hierarchy is valid for rendering, along with all the changes applied by
|
||||
its inherited views, by rendering it using ``fields_view_get()``.
|
||||
|
||||
@param browse_record view: view to validate
|
||||
@return: the rendered definition (arch) of the view, always utf-8 bytestring (legacy convention)
|
||||
if no error occurred, else False.
|
||||
"""
|
||||
try:
|
||||
fvg = self.pool.get(view.model).fields_view_get(cr, uid, view_id=view.id, view_type=view.type, context=context)
|
||||
return fvg['arch']
|
||||
except:
|
||||
_logger.exception("Can't render view %s for model: %s", view.xml_id, view.model)
|
||||
return False
|
||||
|
||||
def _check_xml(self, cr, uid, ids, context=None):
|
||||
for view in self.browse(cr, uid, ids, context):
|
||||
# Sanity check: the view should not break anything upon rendering!
|
||||
view_arch_utf8 = self._check_render_view(cr, uid, view, context=context)
|
||||
# always utf-8 bytestring - legacy convention
|
||||
if not view_arch_utf8: return False
|
||||
|
||||
# RNG-based validation is not possible anymore with 7.0 forms
|
||||
# TODO 7.0: provide alternative assertion-based validation of view_arch_utf8
|
||||
view_docs = [etree.fromstring(view_arch_utf8)]
|
||||
if view_docs[0].tag == 'data':
|
||||
# A <data> element is a wrapper for multiple root nodes
|
||||
view_docs = view_docs[0]
|
||||
validator = self._relaxng()
|
||||
for view_arch in view_docs:
|
||||
if (view_arch.get('version') < '7.0') and validator and not validator.validate(view_arch):
|
||||
for error in validator.error_log:
|
||||
_logger.error(tools.ustr(error))
|
||||
return False
|
||||
return True
|
||||
|
||||
_constraints = [
|
||||
@@ -122,20 +136,31 @@ class view(osv.osv):
|
||||
cr.execute('CREATE INDEX ir_ui_view_model_type_inherit_id ON ir_ui_view (model, type, inherit_id)')
|
||||
|
||||
def get_inheriting_views_arch(self, cr, uid, view_id, model, context=None):
|
||||
"""Retrieves the architecture of views that inherit from the given view.
|
||||
"""Retrieves the architecture of views that inherit from the given view, from the sets of
|
||||
views that should currently be used in the system. During the module upgrade phase it
|
||||
may happen that a view is present in the database but the fields it relies on are not
|
||||
fully loaded yet. This method only considers views that belong to modules whose code
|
||||
is already loaded. Custom views defined directly in the database are loaded only
|
||||
after the module initialization phase is completely finished.
|
||||
|
||||
:param int view_id: id of the view whose inheriting views should be retrieved
|
||||
:param str model: model identifier of the view's related model (for double-checking)
|
||||
:rtype: list of tuples
|
||||
:return: [(view_arch,view_id), ...]
|
||||
"""
|
||||
cr.execute("""SELECT
|
||||
arch, id
|
||||
FROM
|
||||
ir_ui_view
|
||||
WHERE
|
||||
inherit_id=%s AND model=%s
|
||||
ORDER BY priority""", (view_id, model))
|
||||
if self.pool._init:
|
||||
# Module init currently in progress, only consider views from modules whose code was already loaded
|
||||
query = """SELECT v.arch, v.id FROM ir_ui_view v LEFT JOIN ir_model_data md ON (md.model = 'ir.ui.view' AND md.res_id = v.id)
|
||||
WHERE v.inherit_id=%s AND v.model=%s AND md.module in %s
|
||||
ORDER BY priority"""
|
||||
query_params = (view_id, model, tuple(self.pool._init_modules))
|
||||
else:
|
||||
# Modules fully loaded, consider all views
|
||||
query = """SELECT v.arch, v.id FROM ir_ui_view v
|
||||
WHERE v.inherit_id=%s AND v.model=%s
|
||||
ORDER BY priority"""
|
||||
query_params = (view_id, model)
|
||||
cr.execute(query, query_params)
|
||||
return cr.fetchall()
|
||||
|
||||
def write(self, cr, uid, ids, vals, context=None):
|
||||
|
||||
@@ -23,8 +23,9 @@ import logging
|
||||
import re
|
||||
import urllib
|
||||
import zipimport
|
||||
import base64
|
||||
|
||||
from openerp import modules, pooler, release, tools
|
||||
from openerp import modules, pooler, release, tools, addons
|
||||
from openerp.tools.parse_version import parse_version
|
||||
from openerp.tools.translate import _
|
||||
from openerp.osv import fields, osv, orm
|
||||
@@ -160,6 +161,18 @@ class module(osv.osv):
|
||||
res[key][k] = "\n".join(sorted(v))
|
||||
return res
|
||||
|
||||
def _get_icon_image(self, cr, uid, ids, field_name=None, arg=None, context=None):
|
||||
res = dict.fromkeys(ids, '')
|
||||
for module in self.browse(cr, uid, ids, context=context):
|
||||
path = addons.get_module_resource(module.name, 'static', 'src', 'img', 'icon.png')
|
||||
if path:
|
||||
image_file = tools.file_open(path, 'rb')
|
||||
try:
|
||||
res[module.id] = image_file.read().encode('base64')
|
||||
finally:
|
||||
image_file.close()
|
||||
return res
|
||||
|
||||
_columns = {
|
||||
'name': fields.char("Technical Name", size=128, readonly=True, required=True, select=True),
|
||||
'category_id': fields.many2one('ir.module.category', 'Category', readonly=True, select=True),
|
||||
@@ -211,6 +224,7 @@ class module(osv.osv):
|
||||
'certificate' : fields.char('Quality Certificate', size=64, readonly=True),
|
||||
'application': fields.boolean('Application', readonly=True),
|
||||
'icon': fields.char('Icon URL', size=128),
|
||||
'icon_image': fields.function(_get_icon_image, string='Icon', type="binary"),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
|
||||
@@ -123,9 +123,8 @@
|
||||
<header>
|
||||
<button name="button_install" states="uninstalled" string="Install" icon="terp-gtk-jump-to-ltr" type="object" class="oe_form_button_hi"/>
|
||||
<button name="button_upgrade" states="installed" string="Upgrade" icon="terp-gtk-go-back-rtl" type="object" class="oe_form_button_hi"/>
|
||||
<button name="button_uninstall" states="installed" string="Uninstall (beta)"
|
||||
icon="terp-dialog-close" type="object"
|
||||
confirm="Do you confirm the uninstallation of this module? This will permanently erase all data currently stored by the module!"/>
|
||||
<button name="button_uninstall" states="installed" string="Uninstall (beta)" icon="terp-dialog-close" type="object"
|
||||
confirm="Do you confirm the uninstallation of this module? This will permanently erase all data currently stored by the module!"/>
|
||||
<button name="button_uninstall_cancel" states="to remove" string="Cancel Uninstall" icon="gtk-cancel" type="object"/>
|
||||
<button name="button_upgrade_cancel" states="to upgrade" string="Cancel Upgrade" icon="gtk-cancel" type="object"/>
|
||||
<button name="button_install_cancel" states="to install" string="Cancel Install" icon="gtk-cancel" type="object"/>
|
||||
@@ -133,7 +132,7 @@
|
||||
</header>
|
||||
<sheet>
|
||||
<div class="oe_right oe_form_button_box">
|
||||
<field name="icon" widget='image'/>
|
||||
<field name="icon_image" widget="image"/>
|
||||
</div>
|
||||
<div class="oe_form_title">
|
||||
<label for="shortdesc" class="oe_form_readonly_hidden"/>
|
||||
@@ -158,7 +157,6 @@
|
||||
</page>
|
||||
<page string="Technical Data">
|
||||
<group col="4">
|
||||
<field name="name"/>
|
||||
<field name="demo"/>
|
||||
<field name="application"/>
|
||||
</group>
|
||||
@@ -170,11 +168,11 @@
|
||||
</field>
|
||||
</page>
|
||||
<page string="Features" attrs="{'invisible':[('state','!=','installed')]}">
|
||||
<separator string="Created Menus"/>
|
||||
<label for="menus_by_module" string="Created Menus"/>
|
||||
<field name="menus_by_module"/>
|
||||
<separator string="Created Views"/>
|
||||
<label for="views_by_module" string="Created Views"/>
|
||||
<field name="views_by_module"/>
|
||||
<separator string="Defined Reports"/>
|
||||
<label for="reports_by_module" string="Defined Reports"/>
|
||||
<field name="reports_by_module"/>
|
||||
</page>
|
||||
</notebook>
|
||||
|
||||
@@ -88,17 +88,6 @@ class res_company(osv.osv):
|
||||
return result
|
||||
|
||||
|
||||
def _get_bank_data(self, cr, uid, ids, field_names, arg, context=None):
|
||||
""" Read the 'address' functional fields. """
|
||||
result = {}
|
||||
for company in self.browse(cr, uid, ids, context=context):
|
||||
r = []
|
||||
for bank in company.bank_ids:
|
||||
if bank.footer:
|
||||
r.append(bank.name_get(context=context)[0][1])
|
||||
result[company.id] = ' | '.join(r)
|
||||
return result
|
||||
|
||||
def _set_address_data(self, cr, uid, company_id, name, value, arg, context=None):
|
||||
""" Write the 'address' functional fields. """
|
||||
company = self.browse(cr, uid, company_id, context=context)
|
||||
@@ -120,7 +109,7 @@ class res_company(osv.osv):
|
||||
'partner_id': fields.many2one('res.partner', 'Partner', required=True),
|
||||
'rml_header1': fields.char('Report Header / Company Slogan', size=200, help="Appears by default on the top right corner of your printed documents."),
|
||||
'rml_footer1': fields.char('General Information Footer', size=200),
|
||||
'rml_footer2': fields.function(_get_bank_data, type="char", string='Bank Accounts Footer', size=250, help="This field is computed automatically based on bank accounts defined, having the display on footer checkbox set."),
|
||||
'rml_footer2': fields.char('Bank Accounts Footer', size=250, help="Write here your bank accounts for customer payments."),
|
||||
'rml_header': fields.text('RML Header', required=True),
|
||||
'rml_header2': fields.text('RML Internal Header', required=True),
|
||||
'rml_header3': fields.text('RML Internal Header', required=True),
|
||||
|
||||
@@ -39,6 +39,19 @@
|
||||
</rng:optional>
|
||||
</rng:define>
|
||||
|
||||
<rng:define name="modifiable">
|
||||
<rng:optional>
|
||||
<!-- @modifiers contains a JSON map unifying the various
|
||||
modifier attributes: @readonly, @required, @invisible.
|
||||
Each attribute is a key, mapped to a JSON list representing
|
||||
a condition expressed as an OpenERP `domain` filter
|
||||
Only some of the modifier keys make sense on some
|
||||
elements, for example <filter> and <group> only support
|
||||
`invisible`. -->
|
||||
<rng:attribute name="modifiers"/>
|
||||
</rng:optional>
|
||||
</rng:define>
|
||||
|
||||
<rng:define name="access_rights">
|
||||
<rng:optional>
|
||||
<rng:attribute name="groups"/>
|
||||
@@ -294,6 +307,8 @@
|
||||
<rng:element name="label">
|
||||
<rng:ref name="overload"/>
|
||||
<rng:ref name="access_rights"/>
|
||||
<rng:ref name="modifiable"/>
|
||||
<rng:optional><rng:attribute name="invisible"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="align"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="nolabel"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="colspan"/></rng:optional>
|
||||
@@ -393,6 +408,7 @@
|
||||
<rng:element name="page">
|
||||
<rng:ref name="overload"/>
|
||||
<rng:ref name="access_rights"/>
|
||||
<rng:ref name="modifiable"/>
|
||||
<rng:optional><rng:attribute name="string"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="name"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="attrs"/></rng:optional>
|
||||
@@ -443,6 +459,8 @@
|
||||
<rng:element name="separator">
|
||||
<rng:ref name="overload"/>
|
||||
<rng:ref name="access_rights"/>
|
||||
<rng:ref name="modifiable"/>
|
||||
<rng:optional><rng:attribute name="invisible"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="name"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="colspan"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="rowspan"/></rng:optional>
|
||||
@@ -508,6 +526,7 @@
|
||||
<rng:attribute name="name" />
|
||||
<rng:ref name="overload"/>
|
||||
<rng:ref name="access_rights"/>
|
||||
<rng:ref name="modifiable"/>
|
||||
<rng:optional><rng:attribute name="domain_filter"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="attrs"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="string"/></rng:optional>
|
||||
@@ -587,6 +606,7 @@
|
||||
<rng:element name="group">
|
||||
<rng:ref name="overload"/>
|
||||
<rng:ref name="access_rights"/>
|
||||
<rng:ref name="modifiable"/>
|
||||
<rng:optional><rng:attribute name="attrs"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="colspan"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="rowspan"/></rng:optional>
|
||||
@@ -598,6 +618,7 @@
|
||||
<rng:optional><rng:attribute name="width"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="name"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="color" /></rng:optional>
|
||||
<rng:optional><rng:attribute name="invisible"/></rng:optional>
|
||||
<rng:ref name="container"/>
|
||||
</rng:element>
|
||||
</rng:define>
|
||||
@@ -640,6 +661,7 @@
|
||||
<rng:element name="button">
|
||||
<rng:ref name="overload"/>
|
||||
<rng:ref name="access_rights"/>
|
||||
<rng:ref name="modifiable"/>
|
||||
<rng:optional><rng:attribute name="attrs"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="invisible"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="name" /></rng:optional>
|
||||
@@ -684,7 +706,9 @@
|
||||
<rng:element name="filter">
|
||||
<rng:ref name="overload"/>
|
||||
<rng:ref name="access_rights"/>
|
||||
<rng:ref name="modifiable"/>
|
||||
<rng:optional><rng:attribute name="attrs"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="invisible"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="name" /></rng:optional>
|
||||
<rng:optional><rng:attribute name="separator" /></rng:optional>
|
||||
<rng:optional><rng:attribute name="icon" /></rng:optional>
|
||||
|
||||
@@ -154,7 +154,7 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules=
|
||||
loaded_modules.append(package.name)
|
||||
if hasattr(package, 'init') or hasattr(package, 'update') or package.state in ('to install', 'to upgrade'):
|
||||
init_module_models(cr, package.name, models)
|
||||
|
||||
pool._init_modules.add(package.name)
|
||||
status['progress'] = float(index) / len(graph)
|
||||
|
||||
# Can't put this line out of the loop: ir.module.module will be
|
||||
|
||||
@@ -48,6 +48,10 @@ class Registry(object):
|
||||
self._store_function = {}
|
||||
self._init = True
|
||||
self._init_parent = {}
|
||||
|
||||
# modules fully loaded (maintained during init phase by `loading` module)
|
||||
self._init_modules = set()
|
||||
|
||||
self.db_name = db_name
|
||||
self.db = openerp.sql_db.db_connect(db_name)
|
||||
|
||||
|
||||
+17
-10
@@ -1977,6 +1977,7 @@ class BaseModel(object):
|
||||
|
||||
def raise_view_error(error_msg, child_view_id):
|
||||
view, child_view = self.pool.get('ir.ui.view').browse(cr, user, [view_id, child_view_id], context)
|
||||
error_msg = error_msg % {'parent_xml_id': view.xml_id}
|
||||
raise AttributeError("View definition error for inherited view '%s' on model '%s': %s"
|
||||
% (child_view.xml_id, self._name, error_msg))
|
||||
|
||||
@@ -2005,16 +2006,18 @@ class BaseModel(object):
|
||||
if node.get('name') == spec.get('name'):
|
||||
return node
|
||||
return None
|
||||
else:
|
||||
for node in source.getiterator(spec.tag):
|
||||
good = True
|
||||
for attr in spec.attrib:
|
||||
if attr != 'position' and (not node.get(attr) or node.get(attr) != spec.get(attr)):
|
||||
good = False
|
||||
break
|
||||
if good:
|
||||
return node
|
||||
return None
|
||||
|
||||
for node in source.getiterator(spec.tag):
|
||||
if isinstance(node, SKIPPED_ELEMENT_TYPES):
|
||||
continue
|
||||
if all(node.get(attr) == spec.get(attr) \
|
||||
for attr in spec.attrib
|
||||
if attr not in ('position','version')):
|
||||
# Version spec should match parent's root element's version
|
||||
if spec.get('version') and spec.get('version') != source.get('version'):
|
||||
return None
|
||||
return node
|
||||
return None
|
||||
|
||||
def apply_inheritance_specs(source, specs_arch, inherit_id=None):
|
||||
""" Apply an inheriting view.
|
||||
@@ -2080,7 +2083,11 @@ class BaseModel(object):
|
||||
if attr != 'position'
|
||||
])
|
||||
tag = "<%s%s>" % (spec.tag, attrs)
|
||||
if spec.get('version') and spec.get('version') != source.get('version'):
|
||||
raise_view_error("Mismatching view API version for element '%s': %r vs %r in parent view '%%(parent_xml_id)s'" % \
|
||||
(tag, spec.get('version'), source.get('version')), inherit_id)
|
||||
raise_view_error("Element '%s' not found in parent view '%%(parent_xml_id)s'" % tag, inherit_id)
|
||||
|
||||
return source
|
||||
|
||||
def apply_view_inheritance(cr, user, source, inherit_id):
|
||||
|
||||
Reference in New Issue
Block a user