From 2b1deeebfcfff78a0a8bdc491bb4984200e21122 Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Wed, 5 Mar 2025 13:06:30 +0000 Subject: [PATCH 1/2] Add test to prove manually sorted titles is broken --- .../tests/registry_test_vocabulary.xml | 14 +++++ .../querystring/tests/testRegistryReader.py | 58 +++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/plone/app/querystring/tests/registry_test_vocabulary.xml b/plone/app/querystring/tests/registry_test_vocabulary.xml index 529d420..7879581 100644 --- a/plone/app/querystring/tests/registry_test_vocabulary.xml +++ b/plone/app/querystring/tests/registry_test_vocabulary.xml @@ -22,4 +22,18 @@ plone.app.querystring.tests.testvocabulary Metadata + + + Manually ordered test field + True + True + + plone.app.querystring.operation.string.is + + plone.app.querystring.tests.testvocabulary_manually_ordered_field + Metadata + + diff --git a/plone/app/querystring/tests/testRegistryReader.py b/plone/app/querystring/tests/testRegistryReader.py index 962ad68..228c1e5 100644 --- a/plone/app/querystring/tests/testRegistryReader.py +++ b/plone/app/querystring/tests/testRegistryReader.py @@ -1,3 +1,4 @@ +from collections import OrderedDict from plone.app.querystring.interfaces import IQuerystringRegistryReader from plone.app.querystring.registryreader import DottedDict from plone.app.querystring.testing import PLONEAPPQUERYSTRING_INTEGRATION_TESTING @@ -19,6 +20,20 @@ def __call__(self, context): return SimpleVocabulary([SimpleVocabulary.createTerm(term, term, term)]) +@implementer(IVocabularyFactory) +class TestVocabularyManuallyOrdered: + def __call__(self, context): + return SimpleVocabulary.fromItems( + ( + ("A", "id_a", "A"), + ("E", "id_e", "E"), + ("B", "id_b", "B"), + ("D", "id_d", "D"), + ("C", "id_c", "C"), + ) + ) + + class TestRegistryReader(unittest.TestCase): layer = PLONEAPPQUERYSTRING_INTEGRATION_TESTING @@ -29,6 +44,11 @@ def setUp(self): IVocabularyFactory, "plone.app.querystring.tests.testvocabulary", ) + gsm.registerUtility( + TestVocabularyManuallyOrdered(), + IVocabularyFactory, + "plone.app.querystring.tests.testvocabulary_manually_ordered_field", + ) def getLogger(self, value): return "plone.app.querystring" @@ -85,6 +105,44 @@ def test_get_vocabularies(self): vocabulary_result = result.get("plone.app.querystring.field.reviewState.values") self.assertEqual(vocabulary_result, {"term": {"title": "term"}}) + def test_vocabulary_order_retained(self): + """tests if getVocabularyValues is returning the correct vocabulary + values in the correct format + """ + registry = self.createRegistry(td.test_vocabulary_xml) + reader = IQuerystringRegistryReader(registry) + result = reader.parseRegistry() + result = reader.getVocabularyValues(result) + vocabulary_result = result.get( + "plone.app.querystring.field.testvocabulary_manually_ordered.values" + ) + + # This first one is just here temporarily to prove that compared ordered dict works with the sorted keys. + self.assertEqual( + vocabulary_result, + OrderedDict( + { + "id_a": {"title": "A"}, + "id_b": {"title": "B"}, + "id_c": {"title": "C"}, + "id_d": {"title": "D"}, + "id_e": {"title": "E"}, + } + ), + ) + self.assertEqual( + vocabulary_result, + OrderedDict( + { + "id_a": {"title": "A"}, + "id_e": {"title": "E"}, + "id_b": {"title": "B"}, + "id_d": {"title": "D"}, + "id_c": {"title": "C"}, + } + ), + ) + def test_get_vocabularies_in_context(self): portal = self.layer["portal"] subsite = portal[portal.invokeFactory("Document", "subsite", title="Subsite")] From 36bac63929cdcd65167fee44c81bbc252af27cb0 Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Wed, 5 Mar 2025 13:22:00 +0000 Subject: [PATCH 2/2] Add a `values_order` key to the `getVocabularyValues` function so that we can un-sort them if we want --- plone/app/querystring/registryreader.py | 5 ++++ .../querystring/tests/testRegistryReader.py | 24 +++++++++++++++---- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/plone/app/querystring/registryreader.py b/plone/app/querystring/registryreader.py index ad6e23f..52401de 100644 --- a/plone/app/querystring/registryreader.py +++ b/plone/app/querystring/registryreader.py @@ -94,6 +94,11 @@ def getVocabularyValues(self, values): else: title = item.title translated.append((title, item.value)) + # We don't need a values_order key if there aren't any values + if translated: + field["values_order"] = [] + for title, value in translated: + field["values_order"].append(value) translated = sorted(translated, key=lambda x: id_normalize(safe_text(x[0]))) for title, value in translated: field["values"][value] = {"title": title} diff --git a/plone/app/querystring/tests/testRegistryReader.py b/plone/app/querystring/tests/testRegistryReader.py index 228c1e5..3c1b841 100644 --- a/plone/app/querystring/tests/testRegistryReader.py +++ b/plone/app/querystring/tests/testRegistryReader.py @@ -113,13 +113,13 @@ def test_vocabulary_order_retained(self): reader = IQuerystringRegistryReader(registry) result = reader.parseRegistry() result = reader.getVocabularyValues(result) - vocabulary_result = result.get( + vocabulary_values = result.get( "plone.app.querystring.field.testvocabulary_manually_ordered.values" ) - # This first one is just here temporarily to prove that compared ordered dict works with the sorted keys. + # This is here to prove that we are getting sorted titles. We'll check for the manual order shortly self.assertEqual( - vocabulary_result, + vocabulary_values, OrderedDict( { "id_a": {"title": "A"}, @@ -130,8 +130,24 @@ def test_vocabulary_order_retained(self): } ), ) + vocabulary_values_order = result.get( + "plone.app.querystring.field.testvocabulary_manually_ordered.values_order" + ) + self.assertEqual( + vocabulary_values_order, + [ + "id_a", + "id_e", + "id_b", + "id_d", + "id_c", + ], + ) self.assertEqual( - vocabulary_result, + # Re-sort the sorted dictionary based on the correct order + OrderedDict( + {token: vocabulary_values[token] for token in vocabulary_values_order} + ), OrderedDict( { "id_a": {"title": "A"},