From c0cf68aca685d2dbbbe8e718d77daacd192b8929 Mon Sep 17 00:00:00 2001 From: faro76 Date: Wed, 27 May 2026 17:08:49 +0200 Subject: [PATCH 1/8] Move plone.app.registry Classic-UI templates/cpanels to plone.app.layout --- src/plone/app/registry/__init__.py | 8 + .../app/registry/browser/controlpanel.py | 87 +- src/plone/app/registry/browser/delete.py | 24 +- src/plone/app/registry/browser/edit.py | 80 +- src/plone/app/registry/browser/exportxml.py | 138 +- src/plone/app/registry/browser/records.py | 239 +-- .../browser/templates/controlpanel_layout.pt | 36 - .../browser/templates/delete_layout.pt | 63 - .../registry/browser/templates/edit_layout.pt | 38 - .../registry/browser/templates/exportxml.pt | 36 - .../app/registry/browser/templates/records.pt | 255 --- src/plone/app/registry/configure.zcml | 1 - .../app/registry/tests/test_controlpanel.py | 103 -- .../app/registry/tests/test_exportimport.py | 1494 ----------------- 14 files changed, 50 insertions(+), 2552 deletions(-) delete mode 100644 src/plone/app/registry/browser/templates/controlpanel_layout.pt delete mode 100644 src/plone/app/registry/browser/templates/delete_layout.pt delete mode 100644 src/plone/app/registry/browser/templates/edit_layout.pt delete mode 100644 src/plone/app/registry/browser/templates/exportxml.pt delete mode 100644 src/plone/app/registry/browser/templates/records.pt delete mode 100644 src/plone/app/registry/tests/test_controlpanel.py delete mode 100644 src/plone/app/registry/tests/test_exportimport.py diff --git a/src/plone/app/registry/__init__.py b/src/plone/app/registry/__init__.py index 2338541..05092a9 100644 --- a/src/plone/app/registry/__init__.py +++ b/src/plone/app/registry/__init__.py @@ -1 +1,9 @@ from plone.app.registry.registry import Registry # noqa: F401 + +jbot_deprecations = { + "plone.app.registry.browser.templates.controlpanel_layout.pt": "plone.app.layout.controlpanel.registry_controlpanel_layout.pt", + "plone.app.registry.browser.templates.delete_layout.pt": "plone.app.layout.controlpanel.registry_delete_layout.pt", + "plone.app.registry.browser.templates.edit_layout.pt": "plone.app.layout.controlpanel.registry_edit_layout.pt", + "plone.app.registry.browser.templates.exportxml.pt": "plone.app.layout.controlpanel.registry_exportxml.pt", + "plone.app.registry.browser.templates.records.pt": "plone.app.layout.controlpanel.registry_records.pt", +} diff --git a/src/plone/app/registry/browser/controlpanel.py b/src/plone/app/registry/browser/controlpanel.py index e152a39..7c33b34 100644 --- a/src/plone/app/registry/browser/controlpanel.py +++ b/src/plone/app/registry/browser/controlpanel.py @@ -1,80 +1,13 @@ -from plone.autoform.form import AutoExtensibleForm -from plone.registry.interfaces import IRegistry -from plone.z3cform import layout -from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile -from Products.statusmessages.interfaces import IStatusMessage -from z3c.form import button -from z3c.form import form -from zope.component import getUtility -from zope.component.hooks import getSite -from zope.i18nmessageid import MessageFactory +import zope.deferredimport -_ = MessageFactory("plone") +zope.deferredimport.initialize() +zope.deferredimport.deprecated( + "Please use from plone.app.layout.controlpanels.registry import RegistryEditForm instead.", + RegistryEditForm="plone.app.layout.controlpanels.registry:RegistryEditForm", +) -class RegistryEditForm(AutoExtensibleForm, form.EditForm): - """Edit a records proxy based on an interface. - - To use this, you should use the element in a registry.xml - GS import step to register records for a particular interface. Then - subclass this form and set the 'schema' class variable to point to - the same interface. You can use plone.autoform form hints to affect the - rendering of the form, or override the update() method as appropriate. - - To get the standard control panel layout, use ControlPanelFormWrapper as - the form wrapper, e.g. - - from plone.app.registry.browser.form import RegistryEditForm - from plone.app.registry.browser.form import ControlPanelFormWrapper - from my.package.interfaces import IMySettings - form plone.z3cform import layout - - class MyForm(RegistryEditForm): - schema = IMySettings - - MyFormView = layout.wrap_form(MyForm, ControlPanelFormWrapper) - - Then register MyFormView as a browser view. - """ - - control_panel_view = "@@overview-controlpanel" - schema_prefix = None - - def getContent(self): - return getUtility(IRegistry).forInterface( - self.schema, prefix=self.schema_prefix - ) - - def updateActions(self): - super().updateActions() - self.actions["save"].addClass("btn btn-primary") - self.actions["cancel"].addClass("btn btn-secondary") - - @button.buttonAndHandler(_("Save"), name="save") - def handleSave(self, action): - data, errors = self.extractData() - if errors: - self.status = self.formErrorsMessage - return - self.applyChanges(data) - IStatusMessage(self.request).addStatusMessage(_("Changes saved."), "info") - self.request.response.redirect(self.request.getURL()) - - @button.buttonAndHandler(_("Cancel"), name="cancel") - def handleCancel(self, action): - IStatusMessage(self.request).addStatusMessage(_("Changes canceled."), "info") - self.request.response.redirect( - f"{getSite().absolute_url()}/{self.control_panel_view}" - ) - - -class ControlPanelFormWrapper(layout.FormWrapper): - """Use this form as the plone.z3cform layout wrapper to get the control - panel layout. - """ - - index = ViewPageTemplateFile("templates/controlpanel_layout.pt") - - @property - def control_panel_url(self): - return f"{getSite().absolute_url()}/@@overview-controlpanel" +zope.deferredimport.deprecated( + "Please use from plone.app.layout.controlpanels.registry import ControlPanelFormWrapper instead.", + ControlPanelFormWrapper="plone.app.layout.controlpanels.registry:ControlPanelFormWrapper", +) diff --git a/src/plone/app/registry/browser/delete.py b/src/plone/app/registry/browser/delete.py index 803dfb8..2ad8403 100644 --- a/src/plone/app/registry/browser/delete.py +++ b/src/plone/app/registry/browser/delete.py @@ -1,20 +1,8 @@ -from Products.Five import BrowserView -from Products.statusmessages.interfaces import IStatusMessage +import zope.deferredimport +zope.deferredimport.initialize() -class RecordDeleteView(BrowserView): - def __call__(self): - if self.request.REQUEST_METHOD == "POST": - name = self.request.form.get("name") - if isinstance(name, list) and len(name) > 0: - name = name[0] - if self.request.form.get("form.buttons.delete"): - if name in self.context: - del self.context.records[name] - messages = IStatusMessage(self.request) - messages.add("Successfully deleted field %s" % name, type="info") - elif self.request.form.get("form.buttons.cancel") and name: - messages = IStatusMessage(self.request) - messages.add("Field %s was not deleted" % name, type="info") - return self.request.response.redirect(self.context.absolute_url()) - return super().__call__() +zope.deferredimport.deprecated( + "Please use from plone.app.layout.controlpanels.registry_records import RecordDeleteView instead.", + RecordDeleteView="plone.app.layout.controlpanels.registry_records:RecordDeleteView", +) diff --git a/src/plone/app/registry/browser/edit.py b/src/plone/app/registry/browser/edit.py index 539c6a7..5a15934 100644 --- a/src/plone/app/registry/browser/edit.py +++ b/src/plone/app/registry/browser/edit.py @@ -1,73 +1,13 @@ -from Acquisition import ImplicitAcquisitionWrapper -from plone.z3cform import layout -from Products.statusmessages.interfaces import IStatusMessage -from z3c.form import button -from z3c.form import field -from z3c.form import form -from zope.i18nmessageid import MessageFactory -from zope.interface import implementer -from zope.publisher.interfaces import IPublishTraverse +import zope.deferredimport -_ = MessageFactory("plone") +zope.deferredimport.initialize() +zope.deferredimport.deprecated( + "Please use from plone.app.layout.controlpanels.registry_records import RecordEditForm instead.", + RecordEditForm="plone.app.layout.controlpanels.registry_records:RecordEditForm", +) -class RecordEditForm(form.EditForm): - """Edit a single record""" - - record = None - - @property - def action(self): - return f"{self.context.absolute_url()}/edit/{self.record.__name__}" - - def getContent(self): - return ImplicitAcquisitionWrapper({"value": self.record.value}, self.context) - - def update(self): - self.fields = field.Fields( - self.record.field, - ) - super().update() - - def updateActions(self): - super().updateActions() - self.actions["save"].addClass("btn btn-primary") - self.actions["cancel"].addClass("btn btn-secondary") - - @property - def label(self): - return _("Edit record: ${name}", mapping={"name": self.record.__name__}) - - @button.buttonAndHandler(_("Save"), name="save") - def handleSave(self, action): - data, errors = self.extractData() - if errors: - self.status = self.formErrorsMessage - return - self.record.value = data["value"] - IStatusMessage(self.request).addStatusMessage(_("Changes saved."), "info") - self.request.response.redirect(self.context.absolute_url()) - - @button.buttonAndHandler(_("Cancel"), name="cancel") - def handleCancel(self, action): - IStatusMessage(self.request).addStatusMessage(_("Edit cancelled."), "info") - self.request.response.redirect(self.context.absolute_url()) - - -@implementer(IPublishTraverse) -class RecordEditView(layout.FormWrapper): - form = RecordEditForm - - def __init__(self, context, request): - super().__init__(context, request) - self.request["disable_border"] = True - - def publishTraverse(self, request, name): - path = self.request["TraversalRequestNameStack"] + [name] - path.reverse() - key = "/".join(path) - del self.request["TraversalRequestNameStack"][:] - record = self.context.records[key] - self.record = record - self.form_instance.record = record - return self +zope.deferredimport.deprecated( + "Please use from plone.app.layout.controlpanels.registry_records import RecordEditView instead.", + RecordEditView="plone.app.layout.controlpanels.registry_records:RecordEditView", +) diff --git a/src/plone/app/registry/browser/exportxml.py b/src/plone/app/registry/browser/exportxml.py index 1bc1b85..bc8fbaf 100644 --- a/src/plone/app/registry/browser/exportxml.py +++ b/src/plone/app/registry/browser/exportxml.py @@ -1,134 +1,8 @@ -from lxml import etree -from plone.registry.interfaces import IRegistry -from Products.Five import BrowserView -from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile -from zope.component import getUtility +import zope.deferredimport -import os +zope.deferredimport.initialize() -_current_dir = os.path.dirname(__file__) - - -def _sort_first_lower(key): - return key[0].lower() - - -class RegistryExporterView(BrowserView): - """this view make sane exports of the registry. - - Main goal is to export in a way, that the output can be reused as - best practice settings - """ - - template = ViewPageTemplateFile( - os.path.join(_current_dir, "templates", "exportxml.pt") - ) - - def __call__(self): - interface = self.request.form.get("interface", None) - name = self.request.form.get("name", None) - if not interface and not name: - return self.template() - return self.export(sinterface=interface, sname=name) - - def interfaces(self): - prefixes = [] - registry = getUtility(IRegistry) - baseurl = "{}/@@configuration_registry_export_xml?interface=".format( - self.context.absolute_url() - ) - for record in registry.records.values(): - if record.interfaceName is None: - continue - name = record.interfaceName - url = f"{baseurl}{record.interfaceName}" - pair = (name, url) - if pair not in prefixes: - prefixes.append(pair) - - return sorted(prefixes, key=_sort_first_lower) - - def prefixes(self): - prefixes = [] - registry = getUtility(IRegistry) - baseurl = "{}/@@configuration_registry_export_xml?".format( - self.context.absolute_url() - ) - for record in registry.records.values(): - if record.interfaceName == record.__name__: - continue - - def add_split(part): - url = f"{baseurl}name={part}" - pair = (part, url) - if pair not in prefixes: - prefixes.append(pair) - if part.rfind("/") > part.rfind("."): - new_parts = part.rsplit("/", 1) - else: - new_parts = part.rsplit(".", 1) - if len(new_parts) > 1: - add_split(new_parts[0]) - - add_split(record.__name__) - return sorted(prefixes, key=_sort_first_lower) - - def export(self, sinterface=None, sname=None): - registry = getUtility(IRegistry) - root = etree.Element("registry") - values = {} # full prefix to valuerecord - interface2values = {} - interface2prefix = {} - for record in registry.records.values(): - if sinterface and sinterface != record.interfaceName: - continue - if sname and not record.__name__.startswith(sname): - continue - prefix, value_key = record.__name__.rsplit(".", 1) - xmlvalue = etree.Element("value") - if record.value is None: - continue - if isinstance(record.value, (list, tuple)): - for element in record.value: - xmlel = etree.SubElement(xmlvalue, "element") - xmlel.text = element - elif isinstance(record.value, bool): - xmlvalue.text = "True" if record.value else "False" - elif isinstance(record.value, str): - xmlvalue.text = record.value - else: - xmlvalue.text = str(record.value) - - if record.interfaceName: - xmlvalue.attrib["key"] = value_key - if record.interfaceName not in interface2values: - interface2values[record.interfaceName] = [] - interface2values[record.interfaceName].append(record.__name__) - interface2prefix[record.interfaceName] = prefix - values[record.__name__] = xmlvalue - - for ifname in sorted(interface2values): - xmlrecord = etree.SubElement(root, "records") - xmlrecord.attrib["interface"] = ifname - xmlrecord.attrib["prefix"] = interface2prefix[ifname] - for value in sorted(interface2values[ifname]): - xmlrecord.append(values.pop(value)) - for name, xmlvalue in values.items(): - xmlrecord = etree.SubElement(root, "records") - xmlrecord.attrib["prefix"] = name - xmlrecord.append(xmlvalue) - - self.request.response.setHeader("Content-Type", "text/xml") - filename = "" - if sinterface: - filename += sinterface - if sinterface and sname: - filename += "_-_" - if sname: - filename += sname - self.request.response.setHeader( - "Content-Disposition", f"attachment; filename={filename}.xml" - ) - return etree.tostring( - root, pretty_print=True, xml_declaration=True, encoding="UTF-8" - ) +zope.deferredimport.deprecated( + "Please use from plone.app.layout.controlpanels.registry import RegistryExporterView instead.", + RegistryExporterView="plone.app.layout.controlpanels.registry:RegistryExporterView", +) diff --git a/src/plone/app/registry/browser/records.py b/src/plone/app/registry/browser/records.py index a626c6e..0e68312 100644 --- a/src/plone/app/registry/browser/records.py +++ b/src/plone/app/registry/browser/records.py @@ -1,232 +1,13 @@ -from lxml.etree import XMLSyntaxError -from plone.app.registry.exportimport.handler import RegistryExporter -from plone.app.registry.exportimport.handler import RegistryImporter -from plone.autoform.form import AutoExtensibleForm -from plone.base import PloneMessageFactory as _ -from plone.base.batch import Batch -from plone.registry import field as registry_field -from plone.registry import Record -from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile -from Products.statusmessages.interfaces import IStatusMessage -from z3c.form import button -from z3c.form import form -from z3c.form.action import ActionErrorOccurred -from z3c.form.interfaces import WidgetActionExecutionError -from zope import schema -from zope.component.hooks import getSite -from zope.event import notify -from zope.interface import Interface -from zope.interface import Invalid -from zope.schema.vocabulary import SimpleVocabulary +import zope.deferredimport -import logging -import re -import string +zope.deferredimport.initialize() -logger = logging.getLogger("plone.app.registry") +zope.deferredimport.deprecated( + "Please use from plone.app.layout.controlpanels.registry_records import IAddFieldForm instead.", + IAddFieldForm="plone.app.layout.controlpanels.registry_records:IAddFieldForm", +) - -def _true(s, v): - return True - - -def _is_in(s, v): - return s in v - - -def _starts_with(s, v): - return v.startswith(s) - - -_okay_prefixes = ["Products", "plone.app", "plone"] - - -class FakeEnv: - def getLogger(self, name): - return logger - - def shouldPurge(self): - return False - - -_valid_field_name_chars = string.ascii_letters + "._" - - -def checkFieldName(val): - # Reuse same regex as in plone.registry.registry._Records to allow dottedname with one '/' - validkey = re.compile( - r"([a-zA-Z][a-zA-Z0-9_-]*)((?:\.[a-zA-Z0-9][a-zA-Z0-9_-]*)*)" - r"([/][a-zA-Z0-9][a-zA-Z0-9_-]*)?((?:\.[a-zA-Z0-9][a-zA-Z0-9_-]*)*)$" - ).match - if not validkey(val): - raise Invalid("Not a valid field name") - return True - - -class IAddFieldForm(Interface): - name = schema.TextLine( - title=_("label_field_name", default="Field Name"), - description='Must be in a format like "plone.my_name". Only letters, periods, underscores and up to one /.', - required=True, - constraint=checkFieldName, - ) - - title = schema.TextLine( - title=_("label_field_title", default="Field Title"), required=True - ) - - field_type = schema.Choice( - title="Field Type", - vocabulary=SimpleVocabulary.fromValues( - [ - "Bytes", - "BytesLine", - "ASCII", - "ASCIILine", - "Text", - "TextLine", - "Bool", - "Int", - "Float", - "Decimal", - "Password", - "Datetime", - "Date", - "Timedelta", - "SourceText", - "URI", - "Id", - "DottedName", - # XXX not supporting these types yet as it requires additional config - # 'Tuple', 'List', 'Set', 'FrozenSet', 'Dict', - ] - ), - ) - - required = schema.Bool(title="Required", default=False) - - -class RecordsControlPanel(AutoExtensibleForm, form.Form): - schema = IAddFieldForm - ignoreContext = True - submitted = False - - template = ViewPageTemplateFile("templates/records.pt") - - @property - def action(self): - return f"{self.context.absolute_url()}#autotoc-item-autotoc-3" - - def updateActions(self): - super().updateActions() - self.actions["addfield"].addClass("btn-primary") - - @button.buttonAndHandler("Add field", name="addfield") - def action_addfield(self, action): - data, errors = self.extractData() - self.submitted = True - if not errors: - field_class = getattr(registry_field, data["field_type"], None) - if field_class is None: - notify( - ActionErrorOccurred( - action, - WidgetActionExecutionError( - "field_type", Invalid("Invalid Field") - ), - ) - ) - return - if data["name"] in self.context: - notify( - ActionErrorOccurred( - action, - WidgetActionExecutionError( - "name", Invalid("Field name already in use") - ), - ) - ) - return - - new_field = field_class(title=data["title"], required=data["required"]) - new_record = Record(new_field) - self.context.records[data["name"]] = new_record - messages = IStatusMessage(self.request) - messages.add("Successfully added field %s" % data["name"], type="info") - return self.request.response.redirect( - "{url}/edit/{field}".format( - url=self.context.absolute_url(), field=data["name"] - ) - ) - - def import_registry(self): - try: - fi = self.request.form["file"] - body = fi.read() - except (AttributeError, KeyError): - messages = IStatusMessage(self.request) - messages.add("Must provide XML file", type="error") - body = None - if body is not None: - importer = RegistryImporter(self.context, FakeEnv()) - try: - importer.importDocument(body) - except XMLSyntaxError: - messages = IStatusMessage(self.request) - messages.add("Must provide valid XML file", type="error") - return self.request.response.redirect(self.context.absolute_url()) - - def export_registry(self): - exporter = RegistryExporter(self.context, FakeEnv()) - body = exporter.exportDocument() - resp = self.request.response - resp.setHeader("Content-type", "text/xml") - resp.setHeader("Content-Disposition", "attachment; filename=registry.xml") - resp.setHeader("Content-Length", len(body)) - return body - - @property - def control_panel_url(self): - return f"{getSite().absolute_url()}/@@overview-controlpanel" - - def __call__(self): - form = self.request.form - if self.request.REQUEST_METHOD == "POST": - if form.get("button.exportregistry"): - return self.export_registry() - if form.get("button.importregistry"): - return self.import_registry() - search = form.get("q") - searchp = form.get("qp") - compare = _is_in - if searchp not in (None, ""): - search = searchp - if search is not None and search.startswith("prefix:"): - search = search[len("prefix:") :] - compare = _starts_with - if not search: - compare = _true - - self.prefixes = {} - self.records = [] - for record in self.context.records.values(): - ifaceName = record.interfaceName - if ifaceName is not None: - recordPrefix = ifaceName.split(".")[-1] - prefixValue = record.interfaceName - else: - prefixValue = record.__name__ - for prefix in _okay_prefixes: - name = record.__name__ - if name.startswith(prefix): - recordPrefix = ".".join( - name.split(".")[: len(prefix.split(".")) + 1] - ) - prefixValue = recordPrefix - break - if recordPrefix not in self.prefixes: - self.prefixes[recordPrefix] = prefixValue - if compare(search, prefixValue) or compare(search, record.__name__): - self.records.append(record) - self.records = Batch(self.records, 15, int(form.get("b_start", "0")), orphan=1) - return super().__call__() +zope.deferredimport.deprecated( + "Please use from plone.app.layout.controlpanels.registry_records import RecordsControlPanel instead.", + RecordsControlPanel="plone.app.layout.controlpanels.registry_records:RecordsControlPanel", +) diff --git a/src/plone/app/registry/browser/templates/controlpanel_layout.pt b/src/plone/app/registry/browser/templates/controlpanel_layout.pt deleted file mode 100644 index 8793140..0000000 --- a/src/plone/app/registry/browser/templates/controlpanel_layout.pt +++ /dev/null @@ -1,36 +0,0 @@ - - - - - -
- -

View Title

-

View Description

- -
- -
- Portal status message -
- -
-
- -
-
- -
- - diff --git a/src/plone/app/registry/browser/templates/delete_layout.pt b/src/plone/app/registry/browser/templates/delete_layout.pt deleted file mode 100644 index 5a9ed41..0000000 --- a/src/plone/app/registry/browser/templates/delete_layout.pt +++ /dev/null @@ -1,63 +0,0 @@ - - - - - -
- -

Delete Record

- -

- Are you certain you want to delete this record? This can not be undone - and can be potentially harmful if you don't know what you are doing. -

- - Record - : ${request/form/name} - -
- -
- - -
-
- -
- - diff --git a/src/plone/app/registry/browser/templates/edit_layout.pt b/src/plone/app/registry/browser/templates/edit_layout.pt deleted file mode 100644 index 1b78991..0000000 --- a/src/plone/app/registry/browser/templates/edit_layout.pt +++ /dev/null @@ -1,38 +0,0 @@ - - - - - -
- -

View Title

- -

- Use the form below to edit the value of this particular record. -

- -
- -
- -
- - diff --git a/src/plone/app/registry/browser/templates/exportxml.pt b/src/plone/app/registry/browser/templates/exportxml.pt deleted file mode 100644 index b5ca15e..0000000 --- a/src/plone/app/registry/browser/templates/exportxml.pt +++ /dev/null @@ -1,36 +0,0 @@ - -

Export parts

-

- Download of a XML-file optimized to be used in a GenericSetup profile of an add-on or policy profile. - It contains only the selected parts. -

- -
-
-

by Interface

- -
-
-

by Prefix

- -
-
-
diff --git a/src/plone/app/registry/browser/templates/records.pt b/src/plone/app/registry/browser/templates/records.pt deleted file mode 100644 index f5c486c..0000000 --- a/src/plone/app/registry/browser/templates/records.pt +++ /dev/null @@ -1,255 +0,0 @@ - - - - - - - - - -
-

Configuration Registry

- -

- The table below shows record currently managed by the configuration - registry. Click on a record to edit it. -

-
-
-
-
-

Records

-
- -
- -
- -
-
- -
- -
-
-
- -
-
-
- - -
-
-
- - - -
-
- - - - -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTitleDescriptionTypeValue
- - - - -
- ( - Delete record - ) -
-
-
-
-
-
-
-
-

Export

-

Will export the entire registry into a single XML file.

-
- -
- -
-
-
-
-
-
-

Import

-
-
- - -
- -
- -
-
-
-
-

Add new record

-

- - -
-
-
-
- - diff --git a/src/plone/app/registry/configure.zcml b/src/plone/app/registry/configure.zcml index ca71767..bfc59ee 100644 --- a/src/plone/app/registry/configure.zcml +++ b/src/plone/app/registry/configure.zcml @@ -13,7 +13,6 @@ - - - - - - -""" - - -class DummyImportContext(BaseDummyImportContext): - _directories = {} - - def listDirectory(self, path): - return self._directories.get(path, []) - - def isDirectory(self, path): - return path in self._directories - - -class ExportImportTest(unittest.TestCase): - layer = zca.UNIT_TESTING - - def setUp(self): - self.site = ObjectManager("plone") - self.registry = Registry("portal_registry") - provideUtility(provides=IRegistry, component=self.registry) - context = xmlconfig.string(configuration, execute=True) - try: - import Zope2.App.zcml - - self._context = Zope2.App.zcml._context - Zope2.App.zcml._context = context - except ImportError: - pass - - def tearDown(self): - try: - import Zope2.App.zcml - - Zope2.App.zcml._context = self._context - except ImportError: - pass - - def assertXmlEquals(self, expected, actual): - expected_tree = etree.XML(expected) - actual_tree = etree.XML(actual) - - if etree.tostring(expected_tree) != etree.tostring(actual_tree): - print() - print("Expected:") - print(prettyXML(expected_tree)) - print() - - print() - print("Actual:") - print(prettyXML(actual_tree)) - print() - - raise AssertionError("XML mismatch") - - -class TestImport(ExportImportTest): - def test_empty_import_no_purge(self): - xml = "" - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": xml} - - self.registry.records["test.export.simple"] = Record( - field.TextLine(title="Simple record", default="N/A"), - value="Sample value", - ) - importRegistry(context) - - self.assertEqual(1, len(self.registry.records)) - - def test_import_purge(self): - xml = "" - context = DummyImportContext(self.site, purge=True) - context._files = {"registry.xml": xml} - - self.registry.records["test.export.simple"] = Record( - field.TextLine(title="Simple record", default="N/A"), - value="Sample value", - ) - importRegistry(context) - - self.assertEqual(0, len(self.registry.records)) - - def test_import_records(self): - xml = """\ - - - -""" - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": xml} - - self.registry.records["test.export.simple"] = Record( - field.TextLine(title="Simple record", default="N/A"), - value="Sample value", - ) - importRegistry(context) - - self.assertEqual(3, len(self.registry.records)) - - self.assertIn("plone.app.registry.tests.data.ITestSettings.name", self.registry) - self.assertIn("plone.app.registry.tests.data.ITestSettings.age", self.registry) - - def test_import_records_disallowed(self): - xml = """\ - - - -""" - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": xml} - - self.registry.records["test.export.simple"] = Record( - field.TextLine(title="Simple record", default="N/A"), - value="Sample value", - ) - - try: - importRegistry(context) - except TypeError: - pass - else: - self.fail() - - def test_import_records_omit(self): - xml = """\ - - - blob - - -""" - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": xml} - - self.registry.records["test.export.simple"] = Record( - field.TextLine(title="Simple record", default="N/A"), - value="Sample value", - ) - importRegistry(context) - - self.assertEqual(3, len(self.registry.records)) - - self.assertIn( - "plone.app.registry.tests.data.ITestSettingsDisallowed.name", self.registry - ) - self.assertIn( - "plone.app.registry.tests.data.ITestSettingsDisallowed.age", self.registry - ) - - def test_import_records_remove(self): - xml = """\ - - - -""" - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": xml} - - importRegistry(context) - - self.assertEqual(2, len(self.registry.records)) - delete_xml = """\ - - - -""" - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": delete_xml} - - importRegistry(context) - - self.assertEqual(0, len(self.registry.records)) - - def test_import_records_delete_deprecated(self): - xml = """\ - - - -""" - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": xml} - - importRegistry(context) - - self.assertEqual(2, len(self.registry.records)) - delete_xml = """\ - - - -""" - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": delete_xml} - - importRegistry(context) - - self.assertEqual(0, len(self.registry.records)) - - def test_import_records_remove_with_omit(self): - xml = """\ - - - -""" - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": xml} - - importRegistry(context) - - self.assertEqual(2, len(self.registry.records)) - delete_xml = """\ - - - name - - -""" - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": delete_xml} - - importRegistry(context) - - self.assertEqual(1, len(self.registry.records)) - - self.assertIn("plone.app.registry.tests.data.ITestSettings.name", self.registry) - self.assertNotIn( - "plone.app.registry.tests.data.ITestSettings.age", self.registry - ) - - def test_import_records_remove_with_value(self): - xml = """\ - - - -""" - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": xml} - - importRegistry(context) - - self.assertEqual(2, len(self.registry.records)) - delete_xml = """\ - - - Spam - - -""" - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": delete_xml} - - self.assertRaises(ValueError, importRegistry, context) - - self.assertEqual(2, len(self.registry.records)) - - def test_import_records_with_prefix(self): - xml = """\ - - - -""" - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": xml} - - importRegistry(context) - - self.assertEqual(2, len(self.registry.records)) - - self.assertIn("plone.app.registry.tests.data.SomethingElse.name", self.registry) - self.assertIn("plone.app.registry.tests.data.SomethingElse.age", self.registry) - - def test_import_records_with_values(self): - xml = """\ - - - Magic - 42 - - -""" - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": xml} - - importRegistry(context) - - self.assertEqual(2, len(self.registry.records)) - - self.assertIn("plone.app.registry.tests.data.SomethingElse.name", self.registry) - self.assertIn("plone.app.registry.tests.data.SomethingElse.age", self.registry) - - self.assertEqual( - self.registry["plone.app.registry.tests.data.SomethingElse.name"], "Magic" - ) - self.assertEqual( - self.registry["plone.app.registry.tests.data.SomethingElse.age"], 42 - ) - - def test_import_records_nonexistant_interface(self): - xml = """\ - - - -""" - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": xml} - - self.assertRaises(ImportError, importRegistry, context) - - def test_import_records_nonexistant_interface_condition_not_installed(self): - xml = """\ - - - -""" - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": xml} - - self.assertRaises(ImportError, importRegistry, context) - - def test_import_value_only(self): - xml = """\ - - - Imported value - - -""" - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": xml} - - self.registry.records["test.export.simple"] = Record( - field.TextLine(title="Simple record", default="N/A"), - value="Sample value", - ) - importRegistry(context) - - self.assertEqual(1, len(self.registry.records)) - self.assertEqual( - "Simple record", self.registry.records["test.export.simple"].field.title - ) - self.assertEqual("Imported value", self.registry["test.export.simple"]) - - def test_import_value_only_condition_installed(self): - xml = """\ - - - Imported value - - -""" - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": xml} - - self.registry.records["test.export.simple"] = Record( - field.TextLine(title="Simple record", default="N/A"), - value="Sample value", - ) - importRegistry(context) - - self.assertEqual(1, len(self.registry.records)) - self.assertEqual( - "Simple record", self.registry.records["test.export.simple"].field.title - ) - self.assertEqual("Sample value", self.registry["test.export.simple"]) - - def test_import_value_only_condition_have(self): - xml = """\ - - - Imported value - - -""" - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": xml} - - self.registry.records["test.export.simple"] = Record( - field.TextLine(title="Simple record", default="N/A"), - value="Sample value", - ) - importRegistry(context) - - self.assertEqual(1, len(self.registry.records)) - self.assertEqual( - "Simple record", self.registry.records["test.export.simple"].field.title - ) - self.assertEqual("Imported value", self.registry["test.export.simple"]) - - def test_import_value_only_condition_not_have(self): - xml = """\ - - - Imported value - - -""" - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": xml} - - self.registry.records["test.export.simple"] = Record( - field.TextLine(title="Simple record", default="N/A"), - value="Sample value", - ) - importRegistry(context) - - self.assertEqual(1, len(self.registry.records)) - self.assertEqual( - "Simple record", self.registry.records["test.export.simple"].field.title - ) - self.assertEqual("Sample value", self.registry["test.export.simple"]) - - def test_import_interface_and_value(self): - xml = """\ - - - 2 - - -""" - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": xml} - - importRegistry(context) - - self.assertEqual(1, len(self.registry.records)) - self.assertEqual( - "Age", - self.registry.records[ - "plone.app.registry.tests.data.ITestSettingsDisallowed.age" - ].field.title, - ) - self.assertEqual( - 2, - self.registry["plone.app.registry.tests.data.ITestSettingsDisallowed.age"], - ) - - def test_import_interface_with_differnet_name(self): - xml = """\ - - - 2 - - -""" - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": xml} - - importRegistry(context) - - self.assertEqual(1, len(self.registry.records)) - self.assertEqual( - "Age", self.registry.records["plone.registry.oops"].field.title - ) - self.assertEqual(2, self.registry["plone.registry.oops"]) - - def test_import_interface_no_value(self): - xml = """\ - - - -""" - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": xml} - - importRegistry(context) - - self.assertEqual(1, len(self.registry.records)) - self.assertEqual( - "Name", - self.registry.records[ - "plone.app.registry.tests.data.ITestSettingsDisallowed.name" - ].field.title, - ) - self.assertEqual( - "Mr. Registry", - self.registry["plone.app.registry.tests.data.ITestSettingsDisallowed.name"], - ) - - def test_import_field_only(self): - xml = """\ - - - - N/A - Simple record - - - -""" - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": xml} - - importRegistry(context) - - self.assertEqual(1, len(self.registry.records)) - self.assertTrue( - isinstance( - self.registry.records["test.registry.field"].field, field.TextLine - ) - ) - self.assertEqual( - "Simple record", self.registry.records["test.registry.field"].field.title - ) - self.assertEqual( - "value", self.registry.records["test.registry.field"].field.__name__ - ) - self.assertEqual("N/A", self.registry["test.registry.field"]) - - def test_import_field_ref(self): - xml = """\ - - - - Another value - - -""" - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": xml} - - self.registry.records["test.registry.field"] = Record( - field.TextLine(title="Simple record", default="N/A"), - value="Sample value", - ) - - importRegistry(context) - - self.assertEqual(2, len(self.registry.records)) - self.assertTrue( - IFieldRef.providedBy( - self.registry.records["test.registry.field.override"].field - ) - ) - self.assertEqual( - "Simple record", - self.registry.records["test.registry.field.override"].field.title, - ) - self.assertEqual( - "value", - self.registry.records["test.registry.field.override"].field.__name__, - ) - self.assertEqual("Another value", self.registry["test.registry.field.override"]) - - def test_import_field_and_interface(self): - xml = """\ - - - - N/A - Simple record - - - -""" - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": xml} - - importRegistry(context) - - self.assertEqual(1, len(self.registry.records)) - self.assertTrue( - isinstance( - self.registry.records["test.registry.field"].field, field.ASCIILine - ) - ) - self.assertEqual( - "Simple record", self.registry.records["test.registry.field"].field.title - ) - self.assertEqual("N/A", self.registry["test.registry.field"]) - - def test_import_overwrite_field_with_field(self): - xml = """\ - - - - Nada - Simple record - - - -""" - - self.registry.records["test.registry.field"] = Record( - field.TextLine(title="Simple record", default="N/A"), value="Old value" - ) - - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": xml} - - importRegistry(context) - - self.assertEqual(1, len(self.registry.records)) - self.assertTrue( - isinstance( - self.registry.records["test.registry.field"].field, field.ASCIILine - ) - ) - self.assertEqual( - "Simple record", self.registry.records["test.registry.field"].field.title - ) - self.assertEqual("Nada", self.registry["test.registry.field"]) - - def test_import_overwrite_field_with_interface(self): - xml = """\ - - - -""" - - self.registry.records["test.registry.field"] = Record( - field.TextLine(title="Simple record", default="N/A"), value="Old value" - ) - - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": xml} - - importRegistry(context) - - self.assertEqual(1, len(self.registry.records)) - self.assertTrue( - isinstance(self.registry.records["test.registry.field"].field, field.Int) - ) - self.assertEqual( - "Age", self.registry.records["test.registry.field"].field.title - ) - self.assertEqual(None, self.registry["test.registry.field"]) - - def test_import_collection_field(self): - xml = """\ - - - - Simple record - - 1 - 3 - - - Value - - - - -""" - - self.registry.records["test.registry.field"] = Record( - field.TextLine(title="Simple record", default="N/A"), value="Old value" - ) - - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": xml} - - importRegistry(context) - - self.assertEqual(1, len(self.registry.records)) - self.assertTrue( - isinstance( - self.registry.records["test.registry.field"].field, field.FrozenSet - ) - ) - self.assertEqual( - "Simple record", self.registry.records["test.registry.field"].field.title - ) - self.assertEqual(frozenset([1, 3]), self.registry["test.registry.field"]) - - def test_import_collection_value(self): - xml = """\ - - - - 4 - 6 - - - -""" - - self.registry.records["test.registry.field"] = Record( - field.Set(title="Simple record", value_type=field.Int(title="Val")), - value={1}, - ) - - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": xml} - - importRegistry(context) - - self.assertEqual(1, len(self.registry.records)) - self.assertTrue( - isinstance(self.registry.records["test.registry.field"].field, field.Set) - ) - self.assertEqual( - "Simple record", self.registry.records["test.registry.field"].field.title - ) - self.assertEqual(frozenset([4, 6]), self.registry["test.registry.field"]) - - def test_import_collection_nopurge(self): - xml = """\ - - - - 4 - 6 - - - -""" - - self.registry.records["test.registry.field"] = Record( - field.Set(title="Simple record", value_type=field.Int(title="Val")), - value={1}, - ) - - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": xml} - - importRegistry(context) - - self.assertEqual(1, len(self.registry.records)) - self.assertTrue( - isinstance(self.registry.records["test.registry.field"].field, field.Set) - ) - self.assertEqual( - "Simple record", self.registry.records["test.registry.field"].field.title - ) - self.assertEqual(frozenset([1, 4, 6]), self.registry["test.registry.field"]) - - def test_import_collection_list_append(self): - xml = """\ - - - - 4 - 6 - - - -""" - - self.registry.records["test.registry.field"] = Record( - field.List(title="Simple record", value_type=field.Int(title="Val")), - value=[2, 4], - ) - - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": xml} - - importRegistry(context) - - self.assertEqual(1, len(self.registry.records)) - self.assertEqual([2, 4, 6], self.registry["test.registry.field"]) - - def test_import_collection_tuple_append(self): - xml = """\ - - - - b - c - - - -""" - - self.registry.records["test.registry.field"] = Record( - field.Tuple(title="Simple record", value_type=field.TextLine(title="Val")), - value=( - "a", - "b", - ), - ) - - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": xml} - - importRegistry(context) - - self.assertEqual(1, len(self.registry.records)) - self.assertEqual( - ( - "a", - "b", - "c", - ), - self.registry["test.registry.field"], - ) - - def test_import_collection_set_append(self): - xml = """\ - - - - 4 - 6 - - - -""" - - self.registry.records["test.registry.field"] = Record( - field.Set(title="Simple record", value_type=field.Int(title="Val")), - value={2, 4}, - ) - - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": xml} - - importRegistry(context) - - self.assertEqual(1, len(self.registry.records)) - self.assertEqual({2, 4, 6}, self.registry["test.registry.field"]) - - def test_import_collection_frozenset_append(self): - xml = """\ - - - - 4 - 6 - - - -""" - - self.registry.records["test.registry.field"] = Record( - field.FrozenSet(title="Simple record", value_type=field.Int(title="Val")), - value=frozenset([2, 4]), - ) - - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": xml} - - importRegistry(context) - - self.assertEqual(1, len(self.registry.records)) - self.assertEqual(frozenset([2, 4, 6]), self.registry["test.registry.field"]) - - def test_import_dict_field(self): - xml = """\ - - - - Simple record - - 1 - 3 - - - Key - - - Value - - - - -""" - - self.registry.records["test.registry.field"] = Record( - field.TextLine(title="Simple record", default="N/A"), value="Old value" - ) - - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": xml} - - importRegistry(context) - - self.assertEqual(1, len(self.registry.records)) - self.assertTrue( - isinstance(self.registry.records["test.registry.field"].field, field.Dict) - ) - self.assertEqual( - "Simple record", self.registry.records["test.registry.field"].field.title - ) - self.assertEqual({"a": 1, "b": 3}, self.registry["test.registry.field"]) - - def test_import_dict_value(self): - xml = """\ - - - - 4 - 6 - - - -""" - - self.registry.records["test.registry.field"] = Record( - field.Dict( - title="Simple record", - key_type=field.ASCIILine(title="Key"), - value_type=field.Int(title="Val"), - ), - value={"a": 1}, - ) - - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": xml} - - importRegistry(context) - - self.assertEqual(1, len(self.registry.records)) - self.assertTrue( - isinstance(self.registry.records["test.registry.field"].field, field.Dict) - ) - self.assertEqual( - "Simple record", self.registry.records["test.registry.field"].field.title - ) - self.assertEqual({"x": 4, "y": 6}, self.registry["test.registry.field"]) - - def test_import_dict_nopurge(self): - xml = """\ - - - - 4 - 6 - - - -""" - - self.registry.records["test.registry.field"] = Record( - field.Dict( - title="Simple record", - key_type=field.ASCIILine(title="Key"), - value_type=field.Int(title="Val"), - ), - value={"a": 1}, - ) - - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": xml} - - importRegistry(context) - - self.assertEqual(1, len(self.registry.records)) - self.assertTrue( - isinstance(self.registry.records["test.registry.field"].field, field.Dict) - ) - self.assertEqual( - "Simple record", self.registry.records["test.registry.field"].field.title - ) - self.assertEqual({"a": 1, "x": 4, "y": 6}, self.registry["test.registry.field"]) - - def test_import_choice_field(self): - xml = """\ - - - - Simple record - - One - Two - - - - -""" - - self.registry.records["test.registry.field"] = Record( - field.TextLine(title="Simple record", default="N/A"), value="Old value" - ) - - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": xml} - - importRegistry(context) - - self.assertEqual(1, len(self.registry.records)) - self.assertTrue( - isinstance(self.registry.records["test.registry.field"].field, field.Choice) - ) - self.assertEqual( - "Simple record", self.registry.records["test.registry.field"].field.title - ) - self.assertEqual( - ["One", "Two"], - [ - t.value - for t in self.registry.records["test.registry.field"].field.vocabulary - ], - ) - self.assertEqual(None, self.registry["test.registry.field"]) - - def test_import_with_comments(self): - xml = """\ - - - - Magic - 42 - - - - - N/A - - Simple record - - - -""" - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": xml} - - importRegistry(context) - - self.assertEqual(3, len(self.registry.records)) - - self.assertTrue( - isinstance( - self.registry.records["test.registry.field"].field, field.TextLine - ) - ) - self.assertEqual( - "Simple record", self.registry.records["test.registry.field"].field.title - ) - self.assertEqual( - "value", self.registry.records["test.registry.field"].field.__name__ - ) - self.assertEqual("N/A", self.registry["test.registry.field"]) - - self.assertIn("plone.app.registry.tests.data.SomethingElse.name", self.registry) - self.assertIn("plone.app.registry.tests.data.SomethingElse.age", self.registry) - self.assertEqual( - self.registry["plone.app.registry.tests.data.SomethingElse.name"], "Magic" - ) - self.assertEqual( - self.registry["plone.app.registry.tests.data.SomethingElse.age"], 42 - ) - - def test_remove(self): - xml = """\ - - - -""" - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": xml} - - self.registry.records["test.export.simple"] = Record( - field.TextLine(title="Simple record", default="N/A"), - value="Sample value", - ) - importRegistry(context) - - self.assertEqual(0, len(self.registry.records)) - - def test_delete_deprecated(self): - xml = """\ - - - -""" - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": xml} - - self.registry.records["test.export.simple"] = Record( - field.TextLine(title="Simple record", default="N/A"), - value="Sample value", - ) - importRegistry(context) - - self.assertEqual(0, len(self.registry.records)) - - def test_delete_not_found(self): - xml = """\ - - - -""" - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": xml} - - self.registry.records["test.export.simple"] = Record( - field.TextLine(title="Simple record", default="N/A"), - value="Sample value", - ) - importRegistry(context) - - self.assertEqual(1, len(self.registry.records)) - self.assertEqual( - "Simple record", self.registry.records["test.export.simple"].field.title - ) - self.assertEqual("Sample value", self.registry["test.export.simple"]) - - def test_import_folder(self): - xml1 = """\ - - - - N/A - Simple record - - - -""" - xml2 = """\ - - - - N/A - Simple record - - - -""" - xml3 = """\ - - - - N/A - Simple record - - - -""" - context = DummyImportContext(self.site, purge=False) - context._files = { - "registry.xml": xml1, - ".ignored_file": "", - "registry/foo2.xml": xml2, - "registry/foo3.xml": xml3, - } - context._directories = { - "registry": [ - "foo2.xml", - "foo3.xml", - ".ignored_file", - ] - } - importRegistry(context) - - self.assertEqual(3, len(self.registry.records)) - - for idx in range(1, 4): - fieldname = "test.registry.foobar%i" % idx - self.assertTrue( - isinstance(self.registry.records[fieldname].field, field.TextLine) - ) - self.assertEqual( - "Simple record", self.registry.records[fieldname].field.title - ) - self.assertEqual("value", self.registry.records[fieldname].field.__name__) - self.assertEqual("N/A", self.registry[fieldname]) - - def test_import_jsonfield_only(self): - xml = """\ - - - - {} - JSON record - - - -""" - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": xml} - - importRegistry(context) - - self.assertEqual(1, len(self.registry.records)) - self.assertTrue( - isinstance( - self.registry.records["test.registry.field"].field, field.JSONField - ) - ) - self.assertEqual( - "JSON record", self.registry.records["test.registry.field"].field.title - ) - self.assertDictEqual({}, self.registry["test.registry.field"]) - - def test_import_jsonfield_with_value(self): - xml = """\ - - - - {} - JSON record - - {"items":[{"color":"red","value":"one"},{"color":"röd","value":"two"}]} - - -""" - context = DummyImportContext(self.site, purge=False) - context._files = {"registry.xml": xml} - - importRegistry(context) - - self.assertEqual(1, len(self.registry.records)) - self.assertTrue( - isinstance( - self.registry.records["test.registry.field"].field, field.JSONField - ) - ) - self.assertEqual( - "JSON record", self.registry.records["test.registry.field"].field.title - ) - self.assertDictEqual( - { - "items": [ - {"color": "red", "value": "one"}, - {"color": "röd", "value": "two"}, - ] - }, - self.registry["test.registry.field"], - ) - - -class TestExport(ExportImportTest): - def test_export_empty(self): - xml = """""" - context = DummyExportContext(self.site) - exportRegistry(context) - - self.assertEqual("registry.xml", context._wrote[0][0]) - self.assertXmlEquals(xml, context._wrote[0][1]) - - def test_export_simple(self): - xml = """\ - - - - N/A - Simple record - - Sample value - -""" - - self.registry.records["test.export.simple"] = Record( - field.TextLine(title="Simple record", default="N/A"), - value="Sample value", - ) - - context = DummyExportContext(self.site) - exportRegistry(context) - - self.assertEqual("registry.xml", context._wrote[0][0]) - self.assertXmlEquals(xml, context._wrote[0][1]) - - def test_export_with_interface(self): - xml = """\ - - - - 0 - Age - - - - - - Mr. Registry - Name - - Mr. Registry - - - - N/A - Simple record - - Sample value - -""" - - self.registry.records["test.export.simple"] = Record( - field.TextLine(title="Simple record", default="N/A"), - value="Sample value", - ) - - self.registry.registerInterface(data.ITestSettings) - - context = DummyExportContext(self.site) - exportRegistry(context) - - self.assertEqual("registry.xml", context._wrote[0][0]) - self.assertXmlEquals(xml, context._wrote[0][1]) - - def test_export_field_ref(self): - xml = """\ - - - - N/A - Simple record - - Sample value - - - - Another value - -""" - - self.registry.records["test.export.simple"] = refRecord = Record( - field.TextLine(title="Simple record", default="N/A"), - value="Sample value", - ) - - self.registry.records["test.export.simple.override"] = Record( - FieldRef(refRecord.__name__, refRecord.field), value="Another value" - ) - - context = DummyExportContext(self.site) - exportRegistry(context) - - self.assertEqual("registry.xml", context._wrote[0][0]) - self.assertXmlEquals(xml, context._wrote[0][1]) - - def test_export_with_collection(self): - xml = """\ - - - - Simple record - - Val - - - - 2 - - -""" - self.registry.records["test.export.simple"] = Record( - field.List(title="Simple record", value_type=field.Int(title="Val")), - value=[2], - ) - - context = DummyExportContext(self.site) - exportRegistry(context) - - self.assertEqual("registry.xml", context._wrote[0][0]) - self.assertXmlEquals(xml, context._wrote[0][1]) - - def test_export_with_dict(self): - xml = """\ - - - - - - Key - - Dict - - Value - - - - 1 - - -""" - - self.registry.records["test.export.dict"] = Record( - field.Dict( - title="Dict", - default={}, - key_type=field.ASCIILine(title="Key"), - value_type=field.Int(title="Value"), - ), - value={"a": 1}, - ) - - context = DummyExportContext(self.site) - exportRegistry(context) - - self.assertEqual("registry.xml", context._wrote[0][0]) - self.assertXmlEquals(xml, context._wrote[0][1]) - - def test_export_with_choice(self): - xml = """\ - - - - Simple record - dummy.vocab - - - -""" - - self.registry.records["test.export.choice"] = Record( - field.Choice(title="Simple record", vocabulary="dummy.vocab") - ) - - context = DummyExportContext(self.site) - exportRegistry(context) - - self.assertEqual("registry.xml", context._wrote[0][0]) - self.assertXmlEquals(xml, context._wrote[0][1]) - - def test_export_with_missing_schema_does_not_error(self): - xml = """\ - - - - N/A - Simple record - - Sample value - -""" - - self.registry.records["test.export.simple"] = Record( - field.TextLine(title="Simple record", default="N/A"), - value="Sample value", - ) - - # Note: These are nominally read-only! - self.registry.records["test.export.simple"].field.interfaceName = ( - "non.existent.ISchema" - ) - self.registry.records["test.export.simple"].field.fieldName = "blah" - - alsoProvides(self.registry.records["test.export.simple"], IInterfaceAwareRecord) - - context = DummyExportContext(self.site) - exportRegistry(context) - - self.assertEqual("registry.xml", context._wrote[0][0]) - self.assertXmlEquals(xml, context._wrote[0][1]) - - def test_export_with_jsonfield(self): - xml = """\ - - - - {} - Dict - - {'items': [{'color': 'red', 'value': 'one'}, {'color': 'röd', 'value': 'two'}]} - -""" - - self.registry.records["test.export.field"] = Record( - field.JSONField( - title="Dict", - default={}, - ), - value={ - "items": [ - {"color": "red", "value": "one"}, - {"color": "röd", "value": "two"}, - ] - }, - ) - - context = DummyExportContext(self.site) - exportRegistry(context) - - self.assertEqual("registry.xml", context._wrote[0][0]) - self.assertXmlEquals(xml, context._wrote[0][1]) From db8e27a4dddd76b76d074cf9833c949c22576c1b Mon Sep 17 00:00:00 2001 From: faro76 Date: Wed, 27 May 2026 17:09:58 +0200 Subject: [PATCH 2/8] removed deprecated resources folder --- src/plone/app/registry/browser/configure.zcml | 5 --- .../app/registry/browser/resources/icon.png | Bin 267 -> 0 bytes .../app/registry/browser/resources/style.css | 39 ------------------ 3 files changed, 44 deletions(-) delete mode 100644 src/plone/app/registry/browser/resources/icon.png delete mode 100644 src/plone/app/registry/browser/resources/style.css diff --git a/src/plone/app/registry/browser/configure.zcml b/src/plone/app/registry/browser/configure.zcml index 26602d1..836c645 100644 --- a/src/plone/app/registry/browser/configure.zcml +++ b/src/plone/app/registry/browser/configure.zcml @@ -7,11 +7,6 @@ - - Xw=J$KyuG7s@QF*MKqB&B_A8HumAPK}msaeE*pkG3ebVFI z@eC&}*}wdtSG@eing;K;Ne5XaE?x}_dg|W5$dGoE`>o^zlN&%w7(8A5T-G@yGywq6 C7;(=4 diff --git a/src/plone/app/registry/browser/resources/style.css b/src/plone/app/registry/browser/resources/style.css deleted file mode 100644 index 5149225..0000000 --- a/src/plone/app/registry/browser/resources/style.css +++ /dev/null @@ -1,39 +0,0 @@ -/* #searchrow th{ - padding:0; - margin:0; - position: relative; -} - -#searchrow form{ - padding: 5px; -} - -#searchrow input{ - font-size: 16px; - padding: 3px; - width: 100%; -} - -#searchrow select{ - margin-top: 7px; - display: inline-block; - width: inherit; -} - -#searchrow button{ - margin: 1px; - cursor: pointer; - padding: 4px; -} - -#searchrow button a{ - margin: 5px; - color: black; -} - -#clear-filter{ - position: absolute; - bottom: 0; - right: 0; -} - */ From ad76a940704a7c8104bdda26a0edc012f38e8b4c Mon Sep 17 00:00:00 2001 From: faro76 Date: Sun, 31 May 2026 12:04:18 +0200 Subject: [PATCH 3/8] package tested to plone 6.3 only --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index ff84c80..05df208 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,7 @@ "Development Status :: 5 - Production/Stable", "Environment :: Web Environment", "Framework :: Plone", - "Framework :: Plone :: 6.2", + "Framework :: Plone :: 6.3", "Framework :: Plone :: Core", "Framework :: Zope :: 5", "License :: OSI Approved :: GNU General Public License v2 (GPLv2)", From 763096fe7eb1186afabc33da08e15a0a2967ba02 Mon Sep 17 00:00:00 2001 From: faro76 Date: Sun, 31 May 2026 12:25:23 +0200 Subject: [PATCH 4/8] fixed jbot deprecations due to templates folder add in plone.app.layouts --- src/plone/app/registry/__init__.py | 10 +++--- src/plone/app/registry/browser/configure.zcml | 36 ++----------------- 2 files changed, 7 insertions(+), 39 deletions(-) diff --git a/src/plone/app/registry/__init__.py b/src/plone/app/registry/__init__.py index 05092a9..300b377 100644 --- a/src/plone/app/registry/__init__.py +++ b/src/plone/app/registry/__init__.py @@ -1,9 +1,9 @@ from plone.app.registry.registry import Registry # noqa: F401 jbot_deprecations = { - "plone.app.registry.browser.templates.controlpanel_layout.pt": "plone.app.layout.controlpanel.registry_controlpanel_layout.pt", - "plone.app.registry.browser.templates.delete_layout.pt": "plone.app.layout.controlpanel.registry_delete_layout.pt", - "plone.app.registry.browser.templates.edit_layout.pt": "plone.app.layout.controlpanel.registry_edit_layout.pt", - "plone.app.registry.browser.templates.exportxml.pt": "plone.app.layout.controlpanel.registry_exportxml.pt", - "plone.app.registry.browser.templates.records.pt": "plone.app.layout.controlpanel.registry_records.pt", + "plone.app.registry.browser.templates.controlpanel_layout.pt": "plone.app.layout.controlpanel.templates.registry_controlpanel_layout.pt", + "plone.app.registry.browser.templates.delete_layout.pt": "plone.app.layout.controlpanel.templates.registry_delete_layout.pt", + "plone.app.registry.browser.templates.edit_layout.pt": "plone.app.layout.controlpanel.templates.registry_edit_layout.pt", + "plone.app.registry.browser.templates.exportxml.pt": "plone.app.layout.controlpanel.templates.registry_exportxml.pt", + "plone.app.registry.browser.templates.records.pt": "plone.app.layout.controlpanel.templates.registry_records.pt", } diff --git a/src/plone/app/registry/browser/configure.zcml b/src/plone/app/registry/browser/configure.zcml index 836c645..3ebffcb 100644 --- a/src/plone/app/registry/browser/configure.zcml +++ b/src/plone/app/registry/browser/configure.zcml @@ -4,42 +4,10 @@ i18n_domain="plone" > + - - - - - - - - - + From b43316a9f33d1c7d654a0efded8abe4043af950e Mon Sep 17 00:00:00 2001 From: faro76 Date: Sun, 31 May 2026 12:49:04 +0200 Subject: [PATCH 5/8] added news --- news/113.breaking.rst | 2 ++ news/113.internal.rst | 4 ++++ 2 files changed, 6 insertions(+) create mode 100644 news/113.breaking.rst create mode 100644 news/113.internal.rst diff --git a/news/113.breaking.rst b/news/113.breaking.rst new file mode 100644 index 0000000..639bb0b --- /dev/null +++ b/news/113.breaking.rst @@ -0,0 +1,2 @@ +Removed Classic-UI browser templates, control panel views, resources, and +related tests. These have been moved to ``plone.app.layout``. diff --git a/news/113.internal.rst b/news/113.internal.rst new file mode 100644 index 0000000..cf471d5 --- /dev/null +++ b/news/113.internal.rst @@ -0,0 +1,4 @@ +Package configured and tested against Plone 6.3 only. Updated configuration +with ``plone.meta``, added ``uv.lock`` to ``.gitignore``, applied code +formatting, and fixed ``jbot`` deprecations caused by the templates folder +addition in ``plone.app.layouts``. From 99b3fc4427e5b5f9d2dc9c264d6fd692b1ac1288 Mon Sep 17 00:00:00 2001 From: Peter Mathis Date: Tue, 9 Jun 2026 17:13:58 +0200 Subject: [PATCH 6/8] remove obsolete configure.zcml --- src/plone/app/registry/browser/configure.zcml | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 src/plone/app/registry/browser/configure.zcml diff --git a/src/plone/app/registry/browser/configure.zcml b/src/plone/app/registry/browser/configure.zcml deleted file mode 100644 index 3ebffcb..0000000 --- a/src/plone/app/registry/browser/configure.zcml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - From 610b77ee5b11396d4717e000e42d03096a19ffb7 Mon Sep 17 00:00:00 2001 From: Peter Mathis Date: Tue, 9 Jun 2026 17:14:15 +0200 Subject: [PATCH 7/8] register jbot template deprecations --- src/plone/app/registry/configure.zcml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/plone/app/registry/configure.zcml b/src/plone/app/registry/configure.zcml index bfc59ee..bc7cfc1 100644 --- a/src/plone/app/registry/configure.zcml +++ b/src/plone/app/registry/configure.zcml @@ -1,7 +1,9 @@ @@ -12,6 +14,17 @@ + + + + Date: Tue, 9 Jun 2026 17:21:41 +0200 Subject: [PATCH 8/8] fix dependencies --- .meta.toml | 3 +++ pyproject.toml | 1 + setup.py | 5 ----- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.meta.toml b/.meta.toml index 2596a1f..90b92ba 100644 --- a/.meta.toml +++ b/.meta.toml @@ -7,3 +7,6 @@ commit-id = "2.9.0" [tox] test_matrix = {"6.3" = ["*"]} + +[pyproject] +dependencies_ignores = "['z3c.jbot']" diff --git a/pyproject.toml b/pyproject.toml index d6d4b32..3ab6c9a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -122,6 +122,7 @@ Zope = [ ] python-dateutil = ['dateutil'] pytest-plone = ['pytest', 'zope.pytestlayer', 'plone.testing', 'plone.app.testing'] +ignore-packages = ['z3c.jbot'] ## # Add extra configuration options in .meta.toml: diff --git a/setup.py b/setup.py index 05df208..68e74b4 100644 --- a/setup.py +++ b/setup.py @@ -42,20 +42,15 @@ install_requires=[ "lxml", "plone.app.z3cform", - "plone.autoform>=1.0", "plone.base", "plone.registry>=1.0", "plone.supermodel>=1.1", - "Products.statusmessages", "Products.GenericSetup", - "plone.z3cform", - "z3c.form", "Zope", ], extras_require={ "test": [ "plone.app.testing", - "plone.testing", ] }, entry_points="""