def test_export_field_ref(self): xml = """\ <registry> <record name="test.export.simple"> <field type="plone.registry.field.TextLine"> <default>N/A</default> <title>Simple record</title> </field> <value>Sample value</value> </record> <record name="test.export.simple.override"> <field ref="test.export.simple" /> <value>Another value</value> </record> </registry>""" self.registry.records['test.export.simple'] = refRecord = \ Record(field.TextLine(title=u"Simple record", default=u"N/A"), value=u"Sample value") self.registry.records['test.export.simple.override'] = \ Record(FieldRef(refRecord.__name__, refRecord.field), value=u"Another value") context = DummyExportContext(self.site) exportRegistry(context) self.assertEqual('registry.xml', context._wrote[0][0]) self.assertXmlEquals(xml, context._wrote[0][1])
def fix_double_smaxage(context): """Fix caching definition. plone.resource.maxage has title and description from shared maxage. See https://github.com/plone/Products.CMFPlone/issues/1989 """ from plone.registry.interfaces import IPersistentField from plone.registry.record import Record from plone.registry import field from plone.registry import FieldRef from zope.component import queryAdapter registry = getUtility(IRegistry) # If these three registry records are not defined, # we do no fix. maxage = 'plone.app.caching.strongCaching.plone.resource.maxage' def_maxage = 'plone.app.caching.strongCaching.maxage' def_smaxage = 'plone.app.caching.strongCaching.smaxage' for name in (maxage, def_maxage, def_smaxage): if name not in registry: return if registry.records[maxage].field.recordName != def_smaxage: # no fix needed return field_ref = FieldRef(def_maxage, registry.records[def_maxage].field) old_value = registry[maxage] registry.records[maxage] = Record(field_ref, old_value) logger.info('Fixed {} to refer to {}.'.format(maxage, def_maxage))
def applyChanges(self, data): """Save changes in the given data dictionary to the registry. """ for key, value in data.items(): # Lazily create per-ruleset records if necessary if key not in self.registry.records: # This should only ever happen if we have a not-yet-creted # ruleset-specific record assert self.rulesetName in key # Strip the ruleset name out, leaving the original key - this # must exist, otherwise getContent() would not have put it in # the data dictionary globalKey = self.operation.prefix + \ key[len(self.operation.prefix) + len(self.rulesetName) + 1:] assert globalKey in self.registry.records # Create a new record with a FieldRef field = self.registry.records[globalKey].field self.registry.records[key] = Record(FieldRef(globalKey, field), value) else: self.registry[key] = value
def test_fieldref_interfaces(self): from plone.registry import field, FieldRef from plone.registry.interfaces import IFieldRef from zope.schema.interfaces import ICollection listField = field.List(value_type=field.ASCIILine()) ref = FieldRef('some.record', listField) self.assertTrue(ICollection.providedBy(ref)) self.assertTrue(IFieldRef.providedBy(ref))
def _getField(self, name): field = self._fields.get(name, _MARKER) if field is _MARKER: return self.parents._getField(name) if isinstance(field, basestring): recordName = field while isinstance(field, basestring): recordName = field field = self._fields[recordName] field = FieldRef(recordName, field) return field
def test_lookupOption_override(self): provideUtility(Registry(), IRegistry) registry = getUtility(IRegistry) registry.records['plone.caching.tests.test'] = r = Record( field.TextLine(), u'default') registry.records['plone.caching.tests.testrule.test'] = Record( FieldRef(r.__name__, r.field), u'override') result = lookupOption('plone.caching.tests', 'testrule', 'test', default=_marker) self.assertEqual(u'override', result)
def test_fix_double_smaxage(self): from plone.registry.interfaces import IRegistry from plone.registry.record import Record from plone.registry import FieldRef from plone.app.upgrade.v50.final import fix_double_smaxage # Run the upgrade before plone.app.caching is installed, # to check that it does not harm. portal_setup = self.layer['portal'].portal_setup fix_double_smaxage(portal_setup) registry = getUtility(IRegistry) maxage = 'plone.app.caching.strongCaching.plone.resource.maxage' def_maxage = 'plone.app.caching.strongCaching.maxage' def_smaxage = 'plone.app.caching.strongCaching.smaxage' # Install default caching profile. portal_setup.runAllImportStepsFromProfile( 'plone.app.caching:default', ) self.assertTrue(def_maxage in registry) self.assertTrue(def_smaxage in registry) # Run upgrade. fix_double_smaxage(portal_setup) # Install the with-caching-proxy settings. portal_setup.runAllImportStepsFromProfile( 'plone.app.caching:with-caching-proxy', ) # Run upgrade. fix_double_smaxage(portal_setup) # Old situation had maxage referencing the s-maxage definition: field_ref = FieldRef(def_smaxage, registry.records[def_smaxage].field) registry.records[maxage] = Record(field_ref, 999) self.assertEqual(registry.records[maxage].field.recordName, def_smaxage) self.assertEqual(registry[maxage], 999) self.assertIn('shared', registry.records[maxage].field.title.lower()) # Run upgrade. fix_double_smaxage(portal_setup) # Test that this fixes the reference and keeps the value. self.assertEqual(registry.records[maxage].field.recordName, def_maxage) self.assertEqual(registry[maxage], 999) self.assertNotIn('shared', registry.records[maxage].field.title.lower()) # Run upgrade. fix_double_smaxage(portal_setup) self.assertEqual(registry.records[maxage].field.recordName, def_maxage) self.assertEqual(registry[maxage], 999)
def importRecord(self, node): name = node.get('name', '') if node.get('delete') is not None: self.logger.warning(u"The 'delete' attribute of <record /> nodes " u"is deprecated, it should be replaced with " u"'remove'.") remove = node.get('remove', node.get('delete', 'false')) interfaceName = node.get('interface', None) fieldName = node.get('field', None) if not name and (interfaceName and fieldName): prefix = node.get('prefix', None) if prefix is None: prefix = interfaceName name = "%s.%s" % (prefix, fieldName) if not name: raise NameError("No name given for <record /> node!") # Unicode is not supported name = str(name) # Handle deletion and quit if remove.lower() == 'true': if name in self.context.records: del self.context.records[name] self.logger.info("Removed record %s." % name) else: self.logger.warning( "Record %s was marked for deletion, but was not found." % name) return # See if we have an existing record existing_record = self.context.records.get(name, None) interface = None field = None value = _marker value_purge = True # If we are given an interface and field name, try to resolve them if interfaceName and fieldName: try: interface = resolve(interfaceName) field = IPersistentField(interface[fieldName]) except ImportError: self.logger.warning("Failed to import interface %s for \ record %s" % (interfaceName, name)) interface = None field = None except KeyError: self.logger.warning("Interface %s specified for record %s has \ no field %s." % (interfaceName, name, fieldName)) interface = None field = None except TypeError: self.logger.warning("Field %s in interface %s specified for \ record %s cannot be used as a persistent field." % (fieldName, interfaceName, name)) interface = None field = None # Find field and value nodes field_node = None value_node = None for child in node: if not isinstance(child.tag, str): continue elif child.tag.lower() == 'field': field_node = child elif child.tag.lower() == 'value': value_node = child # Let field not potentially override interface[fieldName] if field_node is not None: field_ref = field_node.attrib.get('ref', None) if field_ref is not None: # We have a field reference if field_ref not in self.context: raise KeyError( u"Record %s references field for record %s, \ which does not exist" % (name, field_ref)) ref_record = self.context.records[field_ref] field = FieldRef(field_ref, ref_record.field) else: # We have a standard field field_type = field_node.attrib.get('type', None) field_type_handler = queryUtility(IFieldExportImportHandler, name=field_type) if field_type_handler is None: raise TypeError( "Field of type %s used for record %s is not supported." % (field_type, name)) else: field = field_type_handler.read(field_node) if not IPersistentField.providedBy(field): raise TypeError( "Only persistent fields may be imported. \ %s used for record %s is invalid." % (field_type, name)) if field is not None and not IFieldRef.providedBy(field): # Set interface name and fieldName, if applicable field.interfaceName = interfaceName field.fieldName = fieldName # Fall back to existing record if neither a field node nor the # interface yielded a field change_field = True if field is None and existing_record is not None: change_field = False field = existing_record.field if field is None: raise ValueError("Cannot find a field for the record %s. Add a \ <field /> element or reference an interface and field name." % name) # Extract the value if value_node is not None: value_purge = value_node.attrib.get('purge', '').lower() != 'false' value = elementToValue(field, value_node, default=_marker) # Now either construct or update the record if value is _marker: value = field.default value_purge = True if existing_record is not None: if change_field: existing_record.field = field existing_value = existing_record.value if change_field or value != existing_value: if not value_purge and type(value) == type(existing_value): if isinstance(value, list): value = existing_value + [ v for v in value if v not in existing_value ] elif isinstance(value, tuple): value = existing_value + tuple( [v for v in value if v not in existing_value]) elif isinstance(value, ( set, frozenset, )): value = existing_value.union(value) elif isinstance(value, dict): for key, value in value.items(): # check if value is list, if so, let's add # instead of overridding if type(value) == list: if key in existing_value and \ not shouldPurgeList(value_node, key): existing = existing_value[key] for item in existing: # here, we'll remove existing items # point is that we don't want duplicates # and don't want to reorder if item in value: value.remove(item) existing.extend(value) value = existing existing_value[key] = value value = existing_value existing_record.value = value else: self.context.records[name] = Record(field, value)