Commit 7d31f4fb authored by Arnaud Fontaine's avatar Arnaud Fontaine

ZODB Components: Component with the same version/reference as a validated one...

ZODB Components: Component with the same version/reference as a validated one must not be able to be validated.
parent a450e310
...@@ -123,7 +123,9 @@ class ComponentDynamicPackage(ModuleType): ...@@ -123,7 +123,9 @@ class ComponentDynamicPackage(ModuleType):
# beforehand # beforehand
if version in version_priority_set: if version in version_priority_set:
reference = component.getReference(validated_only=True) reference = component.getReference(validated_only=True)
self.__registry_dict.setdefault(reference, {})[version] = component.getId() self.__registry_dict.setdefault(reference, {})[version] = (
component.getId(),
component.getUid())
return self.__registry_dict return self.__registry_dict
...@@ -281,7 +283,7 @@ class ComponentDynamicPackage(ModuleType): ...@@ -281,7 +283,7 @@ class ComponentDynamicPackage(ModuleType):
(fullname, self._namespace, error)) (fullname, self._namespace, error))
try: try:
component_id = self._registry_dict[name][version] component_id = self._registry_dict[name][version][0]
except KeyError: except KeyError:
raise ImportError("%s: version %s of Component %s could not be found" % \ raise ImportError("%s: version %s of Component %s could not be found" % \
(fullname, version, name)) (fullname, version, name))
...@@ -296,8 +298,9 @@ class ComponentDynamicPackage(ModuleType): ...@@ -296,8 +298,9 @@ class ComponentDynamicPackage(ModuleType):
# Version priority name list is ordered in descending order # Version priority name list is ordered in descending order
for version in site.getVersionPriorityNameList(): for version in site.getVersionPriorityNameList():
component_id = component_version_dict.get(version) component_id_uid_tuple = component_version_dict.get(version)
if component_id is not None: if component_id_uid_tuple is not None:
component_id = component_id_uid_tuple[0]
break break
else: else:
raise ImportError("%s: no version of Component %s in Site priority" % \ raise ImportError("%s: no version of Component %s in Site priority" % \
......
...@@ -177,6 +177,9 @@ class ComponentMixin(PropertyRecordableMixin, Base): ...@@ -177,6 +177,9 @@ class ComponentMixin(PropertyRecordableMixin, Base):
_message_version_not_set = "Version must be set" _message_version_not_set = "Version must be set"
_message_invalid_version = "Version cannot start with '_'" _message_invalid_version = "Version cannot start with '_'"
_message_duplicated_version_reference = "${id} is validated has the same "\
"Reference and Version"
_message_text_content_not_set = "No source code" _message_text_content_not_set = "No source code"
_message_text_content_error = "Error in Source Code: ${error_message}" _message_text_content_error = "Error in Source Code: ${error_message}"
...@@ -195,9 +198,10 @@ class ComponentMixin(PropertyRecordableMixin, Base): ...@@ -195,9 +198,10 @@ class ComponentMixin(PropertyRecordableMixin, Base):
""" """
error_list = super(ComponentMixin, self).checkConsistency(*args, **kw) error_list = super(ComponentMixin, self).checkConsistency(*args, **kw)
object_relative_url = self.getRelativeUrl() object_relative_url = self.getRelativeUrl()
reference = self.getReference() reference = self.getReference()
reference_has_error = False
if not reference: if not reference:
reference_has_error = True
error_list.append( error_list.append(
ConsistencyMessage(self, ConsistencyMessage(self,
object_relative_url, object_relative_url,
...@@ -207,6 +211,7 @@ class ComponentMixin(PropertyRecordableMixin, Base): ...@@ -207,6 +211,7 @@ class ComponentMixin(PropertyRecordableMixin, Base):
elif (reference.endswith('_version') or elif (reference.endswith('_version') or
reference[0] == '_' or reference[0] == '_' or
reference in ('find_module', 'load_module', 'reset')): reference in ('find_module', 'load_module', 'reset')):
reference_has_error = True
error_list.append( error_list.append(
ConsistencyMessage(self, ConsistencyMessage(self,
object_relative_url, object_relative_url,
...@@ -224,6 +229,26 @@ class ComponentMixin(PropertyRecordableMixin, Base): ...@@ -224,6 +229,26 @@ class ComponentMixin(PropertyRecordableMixin, Base):
object_relative_url, object_relative_url,
message=self._message_invalid_version, message=self._message_invalid_version,
mapping={})) mapping={}))
else:
package = __import__(self._getDynamicModuleNamespace(), globals(),
fromlist=[self._getDynamicModuleNamespace()], level=0)
component_id = None
component_uid = None
from Products.ERP5Type.dynamic import aq_method_lock
with aq_method_lock:
component_id_uid_tuple = package._registry_dict.get(
self.getReference(), {}).get(self.getVersion(), None)
if component_id_uid_tuple:
component_id, component_uid = component_id_uid_tuple
if (component_id is not None and component_uid is not None and
not reference_has_error and
component_uid != self.getUid() and component_id != self.getId()):
error_list.append(
ConsistencyMessage(self,
object_relative_url,
message=self._message_duplicated_version_reference,
mapping=dict(id=component_id)))
text_content = self.getTextContent() text_content = self.getTextContent()
if not text_content: if not text_content:
......
...@@ -1261,12 +1261,23 @@ class _TestZodbComponent(SecurityTestCase): ...@@ -1261,12 +1261,23 @@ class _TestZodbComponent(SecurityTestCase):
self._component_tool.reset(force=True, self._component_tool.reset(force=True,
reset_portal_type_at_transaction_boundary=True) reset_portal_type_at_transaction_boundary=True)
@abc.abstractmethod def _newComponent(self, reference, text_content, version='erp5', id_=None):
def _newComponent(self, reference, text_content, version='erp5'):
""" """
Abstract method to create a new Component Create new Component
""" """
pass full_id = '%s.%s.%s' % (self._getComponentModuleName(),
version + '_version',
reference)
if id_ is not None:
full_id += '.%s' % id_
return self._component_tool.newContent(
id=full_id,
version=version,
reference=reference,
text_content=text_content,
portal_type=self._component_portal_type)
@abc.abstractmethod @abc.abstractmethod
def _getComponentModuleName(self): def _getComponentModuleName(self):
...@@ -1895,6 +1906,45 @@ def bar(*args, **kwargs): ...@@ -1895,6 +1906,45 @@ def bar(*args, **kwargs):
self.assertUserCanModifyDocument(user_id, component) self.assertUserCanModifyDocument(user_id, component)
self.assertUserCanDeleteDocument(user_id, component) self.assertUserCanDeleteDocument(user_id, component)
def testValidateComponentWithSameReferenceVersionAlreadyValidated(self):
reference = 'ValidateComponentWithSameReferenceVersionAlreadyValidated'
component = self._newComponent(reference, 'def foo():\n print "ok"')
component.validate()
self.tic()
component_dup = self._newComponent(reference, 'def foo():\n print "ok"',
id_='duplicated')
self.tic()
from Products.DCWorkflow.DCWorkflow import ValidationFailed
self.assertRaises(ValidationFailed,
self.portal.portal_workflow.doActionFor,
component_dup, 'validate_action')
self.assertEquals(component_dup.getValidationState(), 'draft')
component_dup.setReference(reference + '_copy')
component_dup.validate()
self.tic()
component_dup.setReference(reference)
self.tic()
self.assertEquals(component_dup.getValidationState(), 'modified')
self.assertEquals(component_dup.getReference(), reference)
self.assertEquals(component_dup.getReference(validated_only=True),
reference + '_copy')
component_dup.invalidate()
self.tic()
component_dup.setReference(reference)
self.assertRaises(ValidationFailed,
self.portal.portal_workflow.doActionFor,
component_dup, 'validate_action')
self.assertEquals(component_dup.getValidationState(), 'invalidated')
from Products.ERP5Type.Core.ExtensionComponent import ExtensionComponent from Products.ERP5Type.Core.ExtensionComponent import ExtensionComponent
class TestZodbExtensionComponent(_TestZodbComponent): class TestZodbExtensionComponent(_TestZodbComponent):
...@@ -1902,15 +1952,7 @@ class TestZodbExtensionComponent(_TestZodbComponent): ...@@ -1902,15 +1952,7 @@ class TestZodbExtensionComponent(_TestZodbComponent):
Tests specific to ZODB Extension Component (previously defined in bt5 and Tests specific to ZODB Extension Component (previously defined in bt5 and
installed on the filesystem in $INSTANCE_HOME/Extensions) installed on the filesystem in $INSTANCE_HOME/Extensions)
""" """
def _newComponent(self, reference, text_content, version='erp5'): _component_portal_type = 'Extension Component'
return self._component_tool.newContent(
id='%s.%s.%s' % (self._getComponentModuleName(),
version + '_version',
reference),
version=version,
reference=reference,
text_content=text_content,
portal_type='Extension Component')
def _getComponentModuleName(self): def _getComponentModuleName(self):
return ExtensionComponent._getDynamicModuleNamespace() return ExtensionComponent._getDynamicModuleNamespace()
...@@ -1987,14 +2029,7 @@ class TestZodbDocumentComponent(_TestZodbComponent): ...@@ -1987,14 +2029,7 @@ class TestZodbDocumentComponent(_TestZodbComponent):
previously defined in bt5 and installed on the filesystem in previously defined in bt5 and installed on the filesystem in
$INSTANCE_HOME/Document. Later on, Product Documents will also be migrated $INSTANCE_HOME/Document. Later on, Product Documents will also be migrated
""" """
def _newComponent(self, reference, text_content, version='erp5'): _component_portal_type = 'Document Component'
return self._component_tool.newContent(
id='%s.%s.%s' % (self._getComponentModuleName(),
version + '_version', reference),
reference=reference,
version=version,
text_content=text_content,
portal_type='Document Component')
def _getComponentModuleName(self): def _getComponentModuleName(self):
return DocumentComponent._getDynamicModuleNamespace() return DocumentComponent._getDynamicModuleNamespace()
...@@ -2125,14 +2160,7 @@ class TestZodbTestComponent(_TestZodbComponent): ...@@ -2125,14 +2160,7 @@ class TestZodbTestComponent(_TestZodbComponent):
Tests specific to ZODB Test Component (known as Live Tests, and previously Tests specific to ZODB Test Component (known as Live Tests, and previously
defined in bt5 and installed in $INSTANCE_HOME/test) defined in bt5 and installed in $INSTANCE_HOME/test)
""" """
def _newComponent(self, reference, text_content, version='erp5'): _component_portal_type = 'Test Component'
return self._component_tool.newContent(
id='%s.%s.%s' % (self._getComponentModuleName(),
version + '_version', reference),
reference=reference,
version=version,
text_content=text_content,
portal_type='Test Component')
def _getComponentModuleName(self): def _getComponentModuleName(self):
return TestComponent._getDynamicModuleNamespace() return TestComponent._getDynamicModuleNamespace()
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment