diff --git a/.github/workflows/black.yml b/.github/workflows/black.yml
index 9d8d08f4..a57a7f8a 100644
--- a/.github/workflows/black.yml
+++ b/.github/workflows/black.yml
@@ -24,7 +24,8 @@ jobs:
${{ runner.os }}-pip-
- name: install black
- run: pip install black
+ # temporary fix: https://community.plone.org/t/clash-between-isort-and-black-after-pre-commit-autoupdate/22787
+ run: pip install black==25.12.0
- name: run black
run: black src/ --check --diff
diff --git a/CHANGES.rst b/CHANGES.rst
index 85f4547b..1fa4f677 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -6,7 +6,8 @@ Changelog
- Fix bug search event restapi (#148)
[mamico]
-
+- Make AdvancedQuery ranking rules configurable.
+ [cekk]
5.9.3 (2025-11-24)
------------------
diff --git a/src/redturtle/volto/configure.zcml b/src/redturtle/volto/configure.zcml
index 00c6d078..167a029f 100644
--- a/src/redturtle/volto/configure.zcml
+++ b/src/redturtle/volto/configure.zcml
@@ -44,7 +44,13 @@
provides="Products.GenericSetup.interfaces.EXTENSION"
directory="profiles/to_4307"
/>
-
+
\n"
"Language-Team: LANGUAGE \n"
@@ -14,13 +14,25 @@ msgstr ""
"Preferred-Encodings: utf-8 latin1\n"
"Domain: DOMAIN\n"
+#: redturtle/volto/patches.py:62
+msgid "Alternative url path must not be a view."
+msgstr ""
+
+#: redturtle/volto/patches.py:51
+msgid "Alternative url path must start with a slash."
+msgstr ""
+
+#: redturtle/volto/patches.py:92
+msgid "Cannot use a working path as alternative url."
+msgstr ""
+
#: redturtle/volto/configure.zcml:29
msgid "Installs the redturtle.volto add-on."
-msgstr ""
+msgstr "Installa il componente aggiuntivo redturtle.volto."
#: redturtle/volto/profiles/default/registry/criteria.xml
msgid "Metadata"
-msgstr ""
+msgstr "Metadati"
#: redturtle/volto/browser/controlpanel.py:11
#: redturtle/volto/profiles/default/controlpanel.xml
@@ -29,11 +41,19 @@ msgstr "RedTurtle Volto"
#: redturtle/volto/configure.zcml:29
msgid "RedTurtle: Volto"
-msgstr ""
+msgstr "RedTurtle: Volto"
+
+#: redturtle/volto/configure.zcml:46
+msgid "RedTurtle: Volto (to 4307)"
+msgstr "RedTurtle: Volto (to 4307)"
+
+#: redturtle/volto/configure.zcml:53
+msgid "RedTurtle: Volto (to 4700)"
+msgstr "RedTurtle: Volto (to 4700)"
#: redturtle/volto/configure.zcml:38
msgid "RedTurtle: Volto (uninstall)"
-msgstr ""
+msgstr "RedTurtle: Volto (disinstalla)"
#: redturtle/volto/profiles/default/registry/criteria.xml
msgid "Select False to show only elements not excluded from navigation."
@@ -43,27 +63,65 @@ msgstr "Seleziona False per mostrare solo gli elementi non omessi dalla navigazi
msgid "Show elements excluded from navigation"
msgstr "Elementi omessi dalla navigazione"
+#: redturtle/volto/patches.py:64
+msgid "Target path must not be a view."
+msgstr ""
+
+#: redturtle/volto/patches.py:59
+msgid "Target path must start with a slash."
+msgstr ""
+
+#: redturtle/volto/patches.py:74
+msgid "The provided alternative url already exists!"
+msgstr ""
+
+#: redturtle/volto/patches.py:98
+msgid "The provided target object does not exist."
+msgstr ""
+
#: redturtle/volto/configure.zcml:38
msgid "Uninstalls the redturtle.volto add-on."
msgstr ""
+#: redturtle/volto/browser/configure.zcml:82
+msgid "View Document"
+msgstr ""
+
+#: redturtle/volto/patches.py:48
+msgid "You have to enter a target."
+msgstr ""
+
+#: redturtle/volto/patches.py:46
+msgid "You have to enter an alternative url."
+msgstr ""
+
+#. Default: "List of AdvancedQuery ranking rules. Use '__TERM__' for current search term."
+#: redturtle/volto/interfaces.py:48
+msgid "advanced_query_ranking_rules_help"
+msgstr "Una lista di regole di ranking per AdvancedQuery. Usa '__TERM__' per il termine di ricerca corrente."
+
+#. Default: "AdvancedQuery Ranking rules"
+#: redturtle/volto/interfaces.py:45
+msgid "advanced_query_ranking_rules_label"
+msgstr "Regole di ranking per AdvancedQuery"
+
#. Default: "If enabled, users can't create contents with ids that are already used as aliases."
-#: redturtle/volto/interfaces.py:36
+#: redturtle/volto/interfaces.py:61
msgid "check_aliases_in_namechooser_help"
msgstr "Se attivato, alla creazione o rinomina di un contenuto, verrĂ eseguito anche un controllo su eventuali alias presenti (quelli visibili in Gestione URL), ed eventualmente viene impedita la creazione con quell'id."
#. Default: "Disallow ids used in aliases"
-#: redturtle/volto/interfaces.py:32
+#: redturtle/volto/interfaces.py:57
msgid "check_aliases_in_namechooser_label"
msgstr "Controllo degli id anche sugli alias"
#. Default: "If enabled, a custom ranking for SearchableText searches will be used."
-#: redturtle/volto/interfaces.py:23
+#: redturtle/volto/interfaces.py:37
msgid "enable_advanced_query_ranking_help"
msgstr "Se abilitato, verrĂ utilizzato un ranking custom per la ricerca testuale."
#. Default: "Enable AdvancedQuery ranking"
-#: redturtle/volto/interfaces.py:19
+#: redturtle/volto/interfaces.py:33
msgid "enable_advanced_query_ranking_label"
msgstr "Abilita ranking custom con AdvancedQuery"
diff --git a/src/redturtle/volto/locales/redturtle.volto.pot b/src/redturtle/volto/locales/redturtle.volto.pot
index a33cfec1..10c8b737 100644
--- a/src/redturtle/volto/locales/redturtle.volto.pot
+++ b/src/redturtle/volto/locales/redturtle.volto.pot
@@ -1,10 +1,10 @@
-#--- PLEASE EDIT THE LINES BELOW CORRECTLY ---
-#SOME DESCRIPTIVE TITLE.
-#FIRST AUTHOR , YEAR.
+# --- PLEASE EDIT THE LINES BELOW CORRECTLY ---
+# SOME DESCRIPTIVE TITLE.
+# FIRST AUTHOR , YEAR.
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
-"POT-Creation-Date: 2024-03-28 12:42+0000\n"
+"POT-Creation-Date: 2026-01-30 10:25+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI +ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
@@ -17,6 +17,18 @@ msgstr ""
"Preferred-Encodings: utf-8 latin1\n"
"Domain: redturtle.volto\n"
+#: redturtle/volto/patches.py:62
+msgid "Alternative url path must not be a view."
+msgstr ""
+
+#: redturtle/volto/patches.py:51
+msgid "Alternative url path must start with a slash."
+msgstr ""
+
+#: redturtle/volto/patches.py:92
+msgid "Cannot use a working path as alternative url."
+msgstr ""
+
#: redturtle/volto/configure.zcml:29
msgid "Installs the redturtle.volto add-on."
msgstr ""
@@ -34,6 +46,14 @@ msgstr ""
msgid "RedTurtle: Volto"
msgstr ""
+#: redturtle/volto/configure.zcml:46
+msgid "RedTurtle: Volto (to 4307)"
+msgstr ""
+
+#: redturtle/volto/configure.zcml:53
+msgid "RedTurtle: Volto (to 4700)"
+msgstr ""
+
#: redturtle/volto/configure.zcml:38
msgid "RedTurtle: Volto (uninstall)"
msgstr ""
@@ -46,27 +66,65 @@ msgstr ""
msgid "Show elements excluded from navigation"
msgstr ""
+#: redturtle/volto/patches.py:64
+msgid "Target path must not be a view."
+msgstr ""
+
+#: redturtle/volto/patches.py:59
+msgid "Target path must start with a slash."
+msgstr ""
+
+#: redturtle/volto/patches.py:74
+msgid "The provided alternative url already exists!"
+msgstr ""
+
+#: redturtle/volto/patches.py:98
+msgid "The provided target object does not exist."
+msgstr ""
+
#: redturtle/volto/configure.zcml:38
msgid "Uninstalls the redturtle.volto add-on."
msgstr ""
+#: redturtle/volto/browser/configure.zcml:82
+msgid "View Document"
+msgstr ""
+
+#: redturtle/volto/patches.py:48
+msgid "You have to enter a target."
+msgstr ""
+
+#: redturtle/volto/patches.py:46
+msgid "You have to enter an alternative url."
+msgstr ""
+
+#. Default: "List of AdvancedQuery ranking rules. Use '__TERM__' for current search term."
+#: redturtle/volto/interfaces.py:48
+msgid "advanced_query_ranking_rules_help"
+msgstr ""
+
+#. Default: "AdvancedQuery Ranking rules"
+#: redturtle/volto/interfaces.py:45
+msgid "advanced_query_ranking_rules_label"
+msgstr ""
+
#. Default: "If enabled, users can't create contents with ids that are already used as aliases."
-#: redturtle/volto/interfaces.py:36
+#: redturtle/volto/interfaces.py:61
msgid "check_aliases_in_namechooser_help"
msgstr ""
#. Default: "Disallow ids used in aliases"
-#: redturtle/volto/interfaces.py:32
+#: redturtle/volto/interfaces.py:57
msgid "check_aliases_in_namechooser_label"
msgstr ""
#. Default: "If enabled, a custom ranking for SearchableText searches will be used."
-#: redturtle/volto/interfaces.py:23
+#: redturtle/volto/interfaces.py:37
msgid "enable_advanced_query_ranking_help"
msgstr ""
#. Default: "Enable AdvancedQuery ranking"
-#: redturtle/volto/interfaces.py:19
+#: redturtle/volto/interfaces.py:33
msgid "enable_advanced_query_ranking_label"
msgstr ""
diff --git a/src/redturtle/volto/profiles/default/metadata.xml b/src/redturtle/volto/profiles/default/metadata.xml
index 4babeb28..a93da81c 100644
--- a/src/redturtle/volto/profiles/default/metadata.xml
+++ b/src/redturtle/volto/profiles/default/metadata.xml
@@ -1,6 +1,6 @@
- 4600
+ 4700
profile-plone.volto:default
profile-plone.app.caching:with-caching-proxy
diff --git a/src/redturtle/volto/profiles/default/registry/controlpanel.xml b/src/redturtle/volto/profiles/default/registry/controlpanel.xml
index d6d72582..5855a7e1 100644
--- a/src/redturtle/volto/profiles/default/registry/controlpanel.xml
+++ b/src/redturtle/volto/profiles/default/registry/controlpanel.xml
@@ -1,4 +1,12 @@
-
-
-
+
+
+
+ [
+ {"index": "Subject", "value": "__TERM__", "weight": 16},
+ {"index": "Title", "value": "__TERM__", "weight": 8},
+ {"index": "Description", "value": "__TERM__", "weight": 6},
+ {"index": "portal_type", "value": "UnitaOrganizzativa", "weight": 20}
+ ]
+
+
diff --git a/src/redturtle/volto/profiles/to_4700/registry.xml b/src/redturtle/volto/profiles/to_4700/registry.xml
new file mode 100644
index 00000000..bf7f82ad
--- /dev/null
+++ b/src/redturtle/volto/profiles/to_4700/registry.xml
@@ -0,0 +1,12 @@
+
+
+
+ [
+ {"index": "Subject", "value": "__TERM__", "weight": 16},
+ {"index": "Title", "value": "__TERM__", "weight": 8},
+ {"index": "Description", "value": "__TERM__", "weight": 6},
+ {"index": "portal_type", "value": "UnitaOrganizzativa", "weight": 20}
+ ]
+
+
+
diff --git a/src/redturtle/volto/restapi/services/search/get.py b/src/redturtle/volto/restapi/services/search/get.py
index e4d44258..e6f7f320 100644
--- a/src/redturtle/volto/restapi/services/search/get.py
+++ b/src/redturtle/volto/restapi/services/search/get.py
@@ -8,6 +8,7 @@
from redturtle.volto.interfaces import IRedTurtleVoltoSettings
from zope.component import getMultiAdapter
+import json
import logging
@@ -74,28 +75,7 @@ def search(self, query=None):
query = unflatten_dotted_dict(query)
return super(SearchHandler, self).search(query)
- # TODO: mettere i parametri di ranking in registry
- # XXX: il default sul subject ha senso ? (probabilmente no), rivedere eventualmente anche i test
- term = query.get("SearchableText")
-
- sort_on = query.get("sort_on", "")
- if sort_on:
- sort_order = query.get("sort_order", "")
- if not sort_order:
- if sort_on in ["Date", "effective"]:
- sort_order = "desc"
- else:
- sort_order = "asc"
- if sort_order == "reverse":
- sort_order = "desc"
- rs = (query["sort_on"], sort_order)
- else:
- # use custom ranking
- rs = RankByQueries_Sum(
- (Eq("Subject", term), 16),
- (Eq("Title", term), 8),
- (Eq("Description", term), 6),
- )
+ rs = self.get_ranking_scheme(query)
lazy_resultset = self.catalog.evalAdvancedQuery(
# Eq("SearchableText", term), (rs,), **query
And(*queries),
@@ -111,6 +91,57 @@ def search(self, query=None):
return results
return super().search(query)
+ def get_ranking_scheme(self, query):
+ """
+ Read from registry the ranking rules and build the RankByQueries_Sum
+ accordingly.
+ """
+ term = query.get("SearchableText")
+
+ sort_on = query.get("sort_on", "")
+ if sort_on:
+ sort_order = query.get("sort_order", "")
+ if not sort_order:
+ if sort_on in ["Date", "effective"]:
+ sort_order = "desc"
+ else:
+ sort_order = "asc"
+ if sort_order == "reverse":
+ sort_order = "desc"
+ return (query["sort_on"], sort_order)
+
+ try:
+ ranking_json = api.portal.get_registry_record(
+ "advanced_query_ranking_rules",
+ interface=IRedTurtleVoltoSettings,
+ default="[]",
+ )
+ ranking_rules = json.loads(ranking_json)
+ except (ValueError, TypeError, KeyError):
+ logger.error("Invalid or missing JSON in ranking_rules_json configuration")
+ ranking_rules = []
+
+ # Costruiamo dinamicamente la lista per RankByQueries_Sum
+ rank_args = []
+
+ for rule in ranking_rules:
+ index = rule.get("index")
+ raw_value = rule.get("value")
+ weight = rule.get("weight", 0)
+
+ if not index or not raw_value:
+ continue
+
+ if raw_value == "__TERM__":
+ if not term:
+ continue
+ final_value = term
+ else:
+ final_value = raw_value
+
+ rank_args.append((Eq(index, final_value), weight))
+ return RankByQueries_Sum(*rank_args)
+
def get_advanced_search_query(self, query):
if "use_site_search_settings" in query:
del query["use_site_search_settings"]
diff --git a/src/redturtle/volto/setuphandlers.py b/src/redturtle/volto/setuphandlers.py
index 95ea06dc..99b6a82d 100644
--- a/src/redturtle/volto/setuphandlers.py
+++ b/src/redturtle/volto/setuphandlers.py
@@ -13,7 +13,11 @@
class HiddenProfiles(object):
def getNonInstallableProfiles(self):
"""Hide uninstall profile from site-creation and quickinstaller."""
- return ["redturtle.volto:uninstall"]
+ return [
+ "redturtle.volto:uninstall",
+ "redturtle.volto:profile_to_4307",
+ "redturtle.volto:profile_to_4700",
+ ]
# DEPRECATED
diff --git a/src/redturtle/volto/tests/test_advancedsearch.py b/src/redturtle/volto/tests/test_advancedsearch.py
index 5b0268f3..240f6fb0 100644
--- a/src/redturtle/volto/tests/test_advancedsearch.py
+++ b/src/redturtle/volto/tests/test_advancedsearch.py
@@ -10,6 +10,7 @@
from redturtle.volto.testing import REDTURTLE_VOLTO_API_FUNCTIONAL_TESTING
from transaction import commit
+import json
import unittest
@@ -222,6 +223,44 @@ def test_search_use_sort_on_if_in_query_and_ignore_custom_order(
["d1", "f1", "e1"], [item["@id"].split("/")[-1] for item in result["items"]]
)
+ def test_search_with_custom_rank(self):
+ query = {"SearchableText": "foo"}
+
+ # this is a query with default ranking values
+ result = self.api_session.get("/@search", params=query).json()
+ self.assertEqual(result["items_total"], 3)
+ self.assertEqual(
+ ["d1", "f1", "e1"], [item["@id"].split("/")[-1] for item in result["items"]]
+ )
+
+ # now we change ranking rules to have different order: add Event with
+ # weight 20 (higher than others)
+ api.portal.set_registry_record(
+ "advanced_query_ranking_rules",
+ json.dumps(
+ [
+ {"index": "Subject", "value": "__TERM__", "weight": 16},
+ {"index": "Title", "value": "__TERM__", "weight": 8},
+ {"index": "Description", "value": "__TERM__", "weight": 6},
+ {
+ "index": "portal_type",
+ "value": "Event",
+ "weight": 20,
+ },
+ ]
+ ),
+ interface=IRedTurtleVoltoSettings,
+ )
+
+ commit()
+
+ # re-execute query
+ result = self.api_session.get("/@search", params=query).json()
+ self.assertEqual(result["items_total"], 3)
+ self.assertEqual(
+ ["e1", "d1", "f1"], [item["@id"].split("/")[-1] for item in result["items"]]
+ )
+
class AdvancedSearchWithFlagTest(BaseTest):
def test_by_default_flag_is_disabled(self):
diff --git a/src/redturtle/volto/upgrades.py b/src/redturtle/volto/upgrades.py
index d466f259..c2848d1c 100644
--- a/src/redturtle/volto/upgrades.py
+++ b/src/redturtle/volto/upgrades.py
@@ -635,3 +635,12 @@ def to_4600(context):
portal_types = api.portal.get_tool(name="portal_types")
portal_types["Plone Site"].default_view = "homepage_view"
portal_types["Plone Site"].immediate_view = "homepage_view"
+
+
+def to_4700(context):
+ logger.info("Make AdvancedQuery ranking rules configurable")
+ context.runImportStepFromProfile(
+ "profile-redturtle.volto:profile_to_4700",
+ "plone.app.registry",
+ run_dependencies=False,
+ )
diff --git a/src/redturtle/volto/upgrades.zcml b/src/redturtle/volto/upgrades.zcml
index f5bc6972..2d5095c7 100644
--- a/src/redturtle/volto/upgrades.zcml
+++ b/src/redturtle/volto/upgrades.zcml
@@ -280,4 +280,12 @@
destination="4600"
handler=".upgrades.to_4600"
/>
+