diff --git a/.github/workflows/isort.yaml b/.github/workflows/isort.yaml
new file mode 100644
index 00000000..039aeb05
--- /dev/null
+++ b/.github/workflows/isort.yaml
@@ -0,0 +1,10 @@
+name: Run isort
+on:
+ - push
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - uses: isort/isort-action@v1
diff --git a/.isort.cfg b/.isort.cfg
new file mode 100644
index 00000000..dbbfee21
--- /dev/null
+++ b/.isort.cfg
@@ -0,0 +1,3 @@
+[settings]
+profile=black
+force_single_line = true
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 00000000..24d59c04
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,54 @@
+# See https://pre-commit.com for more information
+# See https://pre-commit.com/hooks.html for more hooks
+
+default_language_version:
+ python: python3.9
+
+default_stages: [commit, push]
+
+repos:
+ - repo: https://github.com/pre-commit/pre-commit-hooks
+ rev: v4.4.0
+ hooks:
+ - id: trailing-whitespace
+ - id: end-of-file-fixer
+ - id: check-yaml
+ - repo: https://github.com/psf/black
+ rev: 23.7.0
+ hooks:
+ - id: black
+# args: ["--line-length=88", "--check", "--diff", "--force-exclude=migrations", "src/"]
+ args: ["--line-length=88", "--force-exclude=migrations", "src/"]
+ types: [python]
+ entry: black
+ - repo: https://github.com/PyCQA/flake8.git
+ rev: "6.1.0"
+ hooks:
+ - id: flake8
+ name: flake8
+ entry: flake8
+ types: [python]
+ args: ["--max-complexity=30", "--max-line-length=88", "--ignore=DJ01,DJ08,W503,ANN101", "--exclude=docs/*", "src/", "setup.py"]
+ - repo: https://github.com/pycqa/isort
+ rev: 5.12.0
+ hooks:
+ - id: isort
+ name: isort (python)
+# args: ["--multi-line=3", "--lbt=1", "--trailing-comma", "--force-grid-wrap=0", "--use-parentheses", "--ensure-newline-before-comments", "--line-length=88"]
+ - repo: local
+ hooks:
+ - id: python-check-pdb
+ name: check pdb
+ description: 'PDB check inside code'
+ entry: '^\s?[^#]+\.set_trace\(\)'
+ language: pygrep
+ types: [python]
+ - repo: https://github.com/collective/zpretty
+ rev: 3.1.0
+ hooks:
+ - id: zpretty
+ name: zpretty
+ - repo: https://github.com/regebro/pyroma
+ rev: '4.2'
+ hooks:
+ - id: pyroma
diff --git a/CHANGES.rst b/CHANGES.rst
index 27082bd5..c59de6e6 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -1,7 +1,76 @@
Changelog
=========
-5.2.3 (unreleased)
+5.4.3 (unreleased)
+------------------
+
+- Nothing changed yet.
+
+
+5.4.2 (2024-01-11)
+------------------
+
+- Fix deserializer for relationfield, add lstrip to path object calculation
+ [eikichi18]
+
+
+5.4.1 (2023-12-28)
+------------------
+
+- Fix deserializer for relationfield, use UID instead of @id
+ [eikichi18]
+
+- Isort
+ [folix-01]
+
+- Add monkeypatch to fix @scadeziario-day endpoint
+ [eikichi18]
+
+
+5.4.0 (2023-11-14)
+------------------
+
+- Return error instead of raise Excpetion for BadRequest in querystringsearch
+ [mamico]
+
+- Add upgrade step and setuphandler to fix robots.txt
+ original rules adding 'Allow: /*?expand*'
+ [lucabel]
+
+5.3.0 (2023-10-25)
+------------------
+
+- Fix: the 'fix-link' view has a bug that corrupts links by replacing
+ the current external URL with a URL that is always relative to the
+ site, even when requesting replacement with a link from a different
+ website.
+ [lucabel].
+
+- plone.app.redirector.FourOhFourView.search_for_similar patch to enable conditionally
+ the search for similar
+ [folix-01]
+
+- Set search/querystring-search limit patch only for anonymous users.
+ Auth users can need to perform an higher query (in contents view for example).
+ [cekk]
+
+- Re-apply context UID filter in querystringsearch service (as it is in plone.restapi).
+ [cekk]
+
+5.2.4 (2023-09-26)
+------------------
+
+- Fix the issue in the @translation GET endpoint: If this
+ endpoint is invoked, possibly by a bot, and plone.app.multilingual
+ is not installed, the call will result in an empty search query
+ on the catalog.
+ [lucabel]
+
+- backport https://github.com/plone/Products.CMFPlone/pull/3845
+ fix: avoid searching all users when many_users is flagged
+ [mamico]
+
+5.2.3 (2023-09-21)
------------------
- Max search limit became configurable by env var 'REDTURTLE_VOLTO_MAX_LIMIT_SEARCH', 500 by default.
diff --git a/README.rst b/README.rst
index b2046a45..22a08b05 100644
--- a/README.rst
+++ b/README.rst
@@ -190,6 +190,9 @@ There is a monkeypatch for Events recurrences that fix their duration.
If it works well, we can make a pr in p.a.event.
+There is another monkeypatch for Events recurrences to change the default behavior of start index serializer.
+Now it keeps all dates even if the single date is already passed.
+
Respect locally allowed types on paste
--------------------------------------
@@ -220,6 +223,12 @@ For details see the `pull-request
+
+
+
+
+
+
+
+
diff --git a/src/redturtle/volto/profiles/default/metadata.xml b/src/redturtle/volto/profiles/default/metadata.xml
index 7e649863..ac6727fb 100644
--- a/src/redturtle/volto/profiles/default/metadata.xml
+++ b/src/redturtle/volto/profiles/default/metadata.xml
@@ -1,6 +1,6 @@
- 4200
+ 4301
profile-plone.volto:default
profile-plone.app.caching:with-caching-proxy
diff --git a/src/redturtle/volto/restapi/deserializer/blocks.py b/src/redturtle/volto/restapi/deserializer/blocks.py
index ed19fefa..868c2f4b 100644
--- a/src/redturtle/volto/restapi/deserializer/blocks.py
+++ b/src/redturtle/volto/restapi/deserializer/blocks.py
@@ -1,13 +1,14 @@
# -*- coding: utf-8 -*-
from copy import deepcopy
+
from plone.restapi.behaviors import IBlocks
from plone.restapi.deserializer.blocks import path2uid
from plone.restapi.interfaces import IBlockFieldDeserializationTransformer
from Products.CMFPlone.interfaces import IPloneSiteRoot
-from redturtle.volto.interfaces import IRedturtleVoltoLayer
from zope.component import adapter
from zope.interface import implementer
+from redturtle.volto.interfaces import IRedturtleVoltoLayer
EXCLUDE_KEYS = ["@type", "token", "value", "@id", "query"]
EXCLUDE_TYPES = ["title", "listing", "calendar", "searchEvents"]
diff --git a/src/redturtle/volto/restapi/deserializer/configure.zcml b/src/redturtle/volto/restapi/deserializer/configure.zcml
index 36236474..9398f48a 100644
--- a/src/redturtle/volto/restapi/deserializer/configure.zcml
+++ b/src/redturtle/volto/restapi/deserializer/configure.zcml
@@ -28,6 +28,9 @@
+
+
+
diff --git a/src/redturtle/volto/restapi/deserializer/dxfields.py b/src/redturtle/volto/restapi/deserializer/dxfields.py
index 9a530198..3750c740 100644
--- a/src/redturtle/volto/restapi/deserializer/dxfields.py
+++ b/src/redturtle/volto/restapi/deserializer/dxfields.py
@@ -1,4 +1,7 @@
# -*- coding: utf-8 -*-
+import dateutil
+import lxml
+import pytz
from plone.app.contenttypes.interfaces import ILink
from plone.app.dexterity.behaviors.metadata import IPublication
from plone.app.event.base import default_timezone
@@ -18,7 +21,6 @@
from Products.CMFPlone.utils import safe_unicode
from pytz import timezone
from pytz import utc
-from redturtle.volto.interfaces import IRedturtleVoltoLayer
from z3c.form.interfaces import IDataManager
from zope.component import adapter
from zope.component import getMultiAdapter
@@ -27,9 +29,7 @@
from zope.schema.interfaces import IDatetime
from zope.schema.interfaces import ITextLine
-import dateutil
-import lxml
-import pytz
+from redturtle.volto.interfaces import IRedturtleVoltoLayer
@implementer(IFieldDeserializer)
diff --git a/src/redturtle/volto/restapi/deserializer/relationfield.py b/src/redturtle/volto/restapi/deserializer/relationfield.py
new file mode 100644
index 00000000..0693e119
--- /dev/null
+++ b/src/redturtle/volto/restapi/deserializer/relationfield.py
@@ -0,0 +1,63 @@
+from urllib.parse import urlparse
+
+from plone.dexterity.interfaces import IDexterityContent
+from plone.restapi.deserializer.dxfields import DefaultFieldDeserializer
+from plone.restapi.interfaces import IFieldDeserializer
+from Products.CMFCore.utils import getToolByName
+from z3c.relationfield.interfaces import IRelationChoice
+from zope.component import adapter
+from zope.component import getMultiAdapter
+from zope.component import queryUtility
+from zope.interface import implementer
+from zope.intid.interfaces import IIntIds
+
+from redturtle.volto.interfaces import IRedturtleVoltoLayer
+
+
+@implementer(IFieldDeserializer)
+@adapter(IRelationChoice, IDexterityContent, IRedturtleVoltoLayer)
+class RelationChoiceFieldDeserializer(DefaultFieldDeserializer):
+ def __call__(self, value):
+ obj = None
+ resolved_by = None
+
+ if isinstance(value, dict):
+ # We are trying to deserialize the output of a serialization
+ # which is enhanced, extract it and put it on the loop again
+ value = value["UID"] if value.get("UID", None) else value["@id"]
+
+ if isinstance(value, int):
+ # Resolve by intid
+ intids = queryUtility(IIntIds)
+ obj = intids.queryObject(value)
+ resolved_by = "intid"
+ elif isinstance(value, str):
+ portal = getMultiAdapter(
+ (self.context, self.request), name="plone_portal_state"
+ ).portal()
+ portal_url = portal.absolute_url()
+ if value.startswith(portal_url):
+ # Resolve by URL
+ obj = portal.restrictedTraverse(urlparse(value).path.lstrip("/"), None)
+ resolved_by = "URL"
+ elif value.startswith("/"):
+ # Resolve by path
+ obj = portal.restrictedTraverse(value.lstrip("/"), None)
+ resolved_by = "path"
+ else:
+ # Resolve by UID
+ catalog = getToolByName(self.context, "portal_catalog")
+ brain = catalog(UID=value)
+ if brain:
+ obj = brain[0].getObject()
+ resolved_by = "UID"
+
+ if obj is None:
+ self.request.response.setStatus(400)
+ msg = f"Could not resolve object for {value}"
+ if resolved_by:
+ msg += f" (resolved by {resolved_by})"
+ raise ValueError(msg)
+
+ self.field.validate(obj)
+ return obj
diff --git a/src/redturtle/volto/restapi/search/query.py b/src/redturtle/volto/restapi/search/query.py
index 0f6da732..e5646a0e 100644
--- a/src/redturtle/volto/restapi/search/query.py
+++ b/src/redturtle/volto/restapi/search/query.py
@@ -1,15 +1,16 @@
# -*- coding: utf-8 -*-
+import re
+
from plone.restapi.interfaces import IIndexQueryParser
from plone.restapi.search.query import (
ZCTextIndexQueryParser as BaseZCTextIndexQueryParser,
)
from Products.ZCTextIndex.ZCTextIndex import ZCTextIndex
-from redturtle.volto.interfaces import IRedturtleVoltoLayer
from zope.component import adapter
-from zope.interface import implementer
from zope.interface import Interface
+from zope.interface import implementer
-import re
+from redturtle.volto.interfaces import IRedturtleVoltoLayer
@implementer(IIndexQueryParser)
diff --git a/src/redturtle/volto/restapi/serializer/blocks.py b/src/redturtle/volto/restapi/serializer/blocks.py
index f32fb0a5..ce0b3bc9 100644
--- a/src/redturtle/volto/restapi/serializer/blocks.py
+++ b/src/redturtle/volto/restapi/serializer/blocks.py
@@ -1,17 +1,18 @@
# -*- coding: utf-8 -*-
from copy import deepcopy
+
from plone import api
from plone.restapi.behaviors import IBlocks
from plone.restapi.interfaces import IBlockFieldSerializationTransformer
from plone.restapi.interfaces import ISerializeToJsonSummary
from plone.restapi.serializer.blocks import uid_to_url
from Products.CMFPlone.interfaces import IPloneSiteRoot
-from redturtle.volto.interfaces import IRedturtleVoltoLayer
from zope.component import adapter
from zope.component import getMultiAdapter
from zope.globalrequest import getRequest
from zope.interface import implementer
+from redturtle.volto.interfaces import IRedturtleVoltoLayer
EXCLUDE_KEYS = ["@type"]
EXCLUDE_TYPES = ["title", "listing"]
diff --git a/src/redturtle/volto/restapi/serializer/dxfields.py b/src/redturtle/volto/restapi/serializer/dxfields.py
index d5f81fe2..1565e8a3 100644
--- a/src/redturtle/volto/restapi/serializer/dxfields.py
+++ b/src/redturtle/volto/restapi/serializer/dxfields.py
@@ -1,4 +1,6 @@
# -*- coding: utf-8 -*-
+import re
+
from plone.app.contenttypes.interfaces import ILink
from plone.app.contenttypes.utils import replace_link_variables_by_paths
from plone.app.dexterity.behaviors.metadata import IPublication
@@ -7,14 +9,12 @@
from plone.restapi.serializer.converters import json_compatible
from plone.restapi.serializer.dxfields import DefaultFieldSerializer
from plone.restapi.serializer.utils import uid_to_url
-from redturtle.volto.interfaces import IRedturtleVoltoLayer
from zope.component import adapter
from zope.interface import implementer
from zope.schema.interfaces import IDatetime
from zope.schema.interfaces import ITextLine
-import re
-
+from redturtle.volto.interfaces import IRedturtleVoltoLayer
RESOLVEUID_RE = re.compile(".*?/resolve[Uu]id/([^/]*)/?(.*)$")
diff --git a/src/redturtle/volto/restapi/serializer/summary.py b/src/redturtle/volto/restapi/serializer/summary.py
index 83e70c1c..901fcdb3 100644
--- a/src/redturtle/volto/restapi/serializer/summary.py
+++ b/src/redturtle/volto/restapi/serializer/summary.py
@@ -8,11 +8,11 @@
DefaultJSONSummarySerializer as BaseSerializer,
)
from plone.restapi.serializer.utils import uid_to_url
-from redturtle.volto.interfaces import IRedturtleVoltoLayer
from zope.component import adapter
-from zope.interface import implementer
from zope.interface import Interface
+from zope.interface import implementer
+from redturtle.volto.interfaces import IRedturtleVoltoLayer
EMPTY_STRINGS = ["None"]
diff --git a/src/redturtle/volto/restapi/services/controlpanel.py b/src/redturtle/volto/restapi/services/controlpanel.py
index 3606ef6e..47915d6b 100644
--- a/src/redturtle/volto/restapi/services/controlpanel.py
+++ b/src/redturtle/volto/restapi/services/controlpanel.py
@@ -1,10 +1,11 @@
# -*- coding: utf-8 -*-
from plone.restapi.controlpanels import RegistryConfigletPanel
-from redturtle.volto.interfaces import IRedTurtleVoltoSettings
-from redturtle.volto.interfaces import IRedTurtleVoltoSettingsControlpanel
from zope.component import adapter
-from zope.interface import implementer
from zope.interface import Interface
+from zope.interface import implementer
+
+from redturtle.volto.interfaces import IRedTurtleVoltoSettings
+from redturtle.volto.interfaces import IRedTurtleVoltoSettingsControlpanel
@adapter(Interface, Interface)
diff --git a/src/redturtle/volto/restapi/services/navigation/get.py b/src/redturtle/volto/restapi/services/navigation/get.py
index 4490a013..26c85005 100644
--- a/src/redturtle/volto/restapi/services/navigation/get.py
+++ b/src/redturtle/volto/restapi/services/navigation/get.py
@@ -1,12 +1,12 @@
# -*- coding: utf-8 -*-
-from plone.app.layout.navigation.navtree import buildFolderTree
from plone.app.layout.navigation.navtree import NavtreeStrategyBase
+from plone.app.layout.navigation.navtree import buildFolderTree
from plone.dexterity.interfaces import IDexterityContainer
from plone.restapi.interfaces import IExpandableElement
from plone.restapi.services import Service
from zope.component import adapter
-from zope.interface import implementer
from zope.interface import Interface
+from zope.interface import implementer
@implementer(IExpandableElement)
diff --git a/src/redturtle/volto/restapi/services/querystringsearch/get.py b/src/redturtle/volto/restapi/services/querystringsearch/get.py
index fab8a8a9..72cdaf60 100644
--- a/src/redturtle/volto/restapi/services/querystringsearch/get.py
+++ b/src/redturtle/volto/restapi/services/querystringsearch/get.py
@@ -1,24 +1,27 @@
# -*- coding: utf-8 -*-
+import logging
from datetime import datetime
+from urllib import parse
+
from DateTime import DateTime
+from plone import api
from plone.app.event.base import get_events
from plone.app.querystring import queryparser
from plone.restapi.batching import HypermediaBatch
+from plone.restapi.bbb import IPloneSiteRoot
from plone.restapi.deserializer import json_body
from plone.restapi.exceptions import DeserializationError
from plone.restapi.interfaces import ISerializeToJson
from plone.restapi.interfaces import ISerializeToJsonSummary
from plone.restapi.services import Service
+from plone.restapi.services.querystringsearch.get import SUPPORT_NOT_UUID_QUERIES
from plone.restapi.services.querystringsearch.get import (
QuerystringSearch as BaseQuerystringSearch,
)
-from redturtle.volto.config import MAX_LIMIT
-from urllib import parse
from zExceptions import BadRequest
from zope.component import getMultiAdapter
-import logging
-
+from redturtle.volto.config import MAX_LIMIT
logger = logging.getLogger(__name__)
@@ -37,17 +40,20 @@ def __call__(self):
try:
b_start = int(data.get("b_start", 0))
except ValueError:
- raise BadRequest("Invalid b_start")
+ self.request.response.setStatus(400)
+ return dict(error=dict(type="BadRequest", message="Invalid b_start"))
try:
b_size = int(data.get("b_size", 25))
except ValueError:
- raise BadRequest("Invalid b_size")
+ self.request.response.setStatus(400)
+ return dict(error=dict(type="BadRequest", message="Invalid b_size"))
sort_on = data.get("sort_on", None)
sort_order = data.get("sort_order", None)
# LIMIT PATCH
if not query:
- raise BadRequest("No query supplied")
+ self.request.response.setStatus(400)
+ return dict(error=dict(type="BadRequest", message="No query supplied"))
limit = self.get_limit(data=data)
# END OF LIMIT PATCH
@@ -70,14 +76,12 @@ def __call__(self):
limit=limit,
)
- # PATCH: we disable this query to boost performances on big sites
# Exclude "self" content item from the results when ZCatalog supports NOT UUID
# queries and it is called on a content object.
- # if not IPloneSiteRoot.providedBy(self.context) and SUPPORT_NOT_UUID_QUERIES:
- # querybuilder_parameters.update(
- # dict(custom_query={"UID": {"not": self.context.UID()}})
- # )
- # END OF PATCH
+ if not IPloneSiteRoot.providedBy(self.context) and SUPPORT_NOT_UUID_QUERIES:
+ querybuilder_parameters.update(
+ dict(custom_query={"UID": {"not": self.context.UID()}})
+ )
try:
results = querybuilder(**querybuilder_parameters)
@@ -85,7 +89,8 @@ def __call__(self):
# This can happen if the query has an invalid operation,
# but plone.app.querystring doesn't raise an exception
# with specific info.
- raise BadRequest("Invalid query.")
+ self.request.response.setStatus(400)
+ return dict(error=dict(type="BadRequest", message="Invalid query"))
results = getMultiAdapter((results, self.request), ISerializeToJson)(
fullobjects=fullobjects
@@ -96,27 +101,34 @@ def get_limit(self, data):
"""
If limit is <= 0 or higher than MAX_LIMIT, set it to MAX_LIMIT
"""
+ is_anon = api.user.is_anonymous()
+ default_value = is_anon and MAX_LIMIT or max(1000, MAX_LIMIT)
+ if "limit" not in data:
+ return default_value
try:
- limit = int(data.get("limit", MAX_LIMIT))
- except ValueError:
- raise BadRequest("Invalid limit")
+ limit = int(data.get("limit", default_value))
+ except ValueError as exc:
+ raise BadRequest("Invalid limit") from exc
- if "limit" in data and limit <= 0:
+ if limit <= 0:
del data["limit"]
- limit = MAX_LIMIT
- if limit > MAX_LIMIT:
+ limit = default_value
+ if limit > default_value:
logger.warning(
'[wrong query] limit is too high: "{}". Set to default ({}).'.format(
- data["query"], MAX_LIMIT
+ data["query"], default_value
)
)
- limit = MAX_LIMIT
+ limit = default_value
return limit
def is_event_search(self, query):
"""
Check if we need to perform a custom search with p.a.events method
"""
+ if not query:
+ return False
+
indexes = [x["i"] for x in query]
portal_type_check = False
diff --git a/src/redturtle/volto/restapi/services/search/get.py b/src/redturtle/volto/restapi/services/search/get.py
index 780d584e..302fe42c 100644
--- a/src/redturtle/volto/restapi/services/search/get.py
+++ b/src/redturtle/volto/restapi/services/search/get.py
@@ -4,11 +4,11 @@
from plone.restapi.search.handler import SearchHandler as OriginalHandler
from plone.restapi.search.utils import unflatten_dotted_dict
from plone.restapi.services import Service
+from zope.component import getMultiAdapter
+
from redturtle.volto import logger
from redturtle.volto.config import MAX_LIMIT
from redturtle.volto.interfaces import IRedTurtleVoltoSettings
-from zope.component import getMultiAdapter
-
# search for 'ranking' in 'SearchableText' and rank very high
# when the term is in 'Subject' and high when it is in 'Title'.
@@ -124,26 +124,30 @@ def search(self, query=None):
return super(SearchHandler, self).search(query)
def _parse_query(self, query):
+ """
+ set a max limit for anonymous calls
+ """
query = super()._parse_query(query)
- for idx in ["sort_limit", "b_size"]:
- if idx not in query:
- continue
- value = query.get(idx, MAX_LIMIT)
- if value <= 0:
- logger.warning(
- '[wrong query] {} is wrong: "{}". Set to default ({}).'.format(
- idx, query, MAX_LIMIT
+ if api.user.is_anonymous():
+ for idx in ["sort_limit", "b_size"]:
+ if idx not in query:
+ continue
+ value = query.get(idx, MAX_LIMIT)
+ if value <= 0:
+ logger.warning(
+ '[wrong query] {} is wrong: "{}". Set to default ({}).'.format(
+ idx, query, MAX_LIMIT
+ )
)
- )
- query[idx] = MAX_LIMIT
+ query[idx] = MAX_LIMIT
- if value > MAX_LIMIT:
- logger.warning(
- '[wrong query] {} is too high: "{}". Set to default ({}).'.format(
- idx, query, MAX_LIMIT
+ if value > MAX_LIMIT:
+ logger.warning(
+ '[wrong query] {} is too high: "{}". Set to default ({}).'.format(
+ idx, query, MAX_LIMIT
+ )
)
- )
- query[idx] = MAX_LIMIT
+ query[idx] = MAX_LIMIT
return query
diff --git a/src/redturtle/volto/setuphandlers.py b/src/redturtle/volto/setuphandlers.py
index 51247e31..3eff52a6 100644
--- a/src/redturtle/volto/setuphandlers.py
+++ b/src/redturtle/volto/setuphandlers.py
@@ -1,7 +1,12 @@
# -*- coding: utf-8 -*-
+import logging
+
+from plone import api
from Products.CMFPlone.interfaces import INonInstallable
from zope.interface import implementer
+logger = logging.getLogger(__name__)
+
@implementer(INonInstallable)
class HiddenProfiles(object):
@@ -10,8 +15,56 @@ def getNonInstallableProfiles(self):
return ["redturtle.volto:uninstall"]
+def upgrade_robots_txt(context):
+ robots = api.portal.get_registry_record("plone.robots_txt")
+ lines = robots.splitlines()
+
+ googlebot_user_agent = "User-Agent: Googlebot"
+ # I saw this writed also as googlebot, so:
+ normalized_google_bot_user_agent = googlebot_user_agent.lower().replace(" ", "")
+
+ useragent_string = "User-Agent: "
+ normalized_useragent_string = useragent_string.lower().replace(" ", "")
+
+ googlebot_allow_rule = "Allow: /*?expand*"
+
+ googlebot_index = -1
+ allow_rule_present = False
+ for i, line in enumerate(lines):
+ if line.lower().replace(" ", "") == normalized_google_bot_user_agent:
+ googlebot_index = i
+ elif (
+ googlebot_index != -1
+ and line.replace(" ", "").lower() # noqa
+ == googlebot_allow_rule.replace(" ", "").lower() # noqa
+ ):
+ allow_rule_present = True
+
+ if googlebot_index != -1 and not allow_rule_present:
+ # Trova l'indice della fine della sezione User-Agent: Googlebot
+ end_googlebot_index = googlebot_index + 1
+ while end_googlebot_index < len(lines) and not lines[
+ end_googlebot_index
+ ].lower().replace(" ", "").startswith(normalized_useragent_string):
+ end_googlebot_index += 1
+
+ # Aggiungi Allow: /*?expand* alla fine della sezione User-Agent: Googlebot
+ if lines[end_googlebot_index - 1] == "":
+ end_googlebot_index -= 1
+ lines.insert(end_googlebot_index, googlebot_allow_rule)
+
+ lines = "\n".join(lines)
+ api.portal.set_registry_record("plone.robots_txt", lines)
+ logger.info("Upgrade robots.txt with rule for googlebot")
+ else:
+ logger.info(
+ "Rule for Googlebot already present in robots.txt, no action needed"
+ )
+
+
def post_install(context):
"""Post install script"""
+ upgrade_robots_txt(context)
def uninstall(context):
diff --git a/src/redturtle/volto/testing.py b/src/redturtle/volto/testing.py
index 81d9eb91..3422c56e 100644
--- a/src/redturtle/volto/testing.py
+++ b/src/redturtle/volto/testing.py
@@ -1,18 +1,18 @@
# -*- coding: utf-8 -*-
+import collective.volto.cookieconsent
+import kitconcept.seo
+import plone.app.caching
+import plone.restapi
+import plone.volto
from plone.app.contenttypes.testing import PLONE_APP_CONTENTTYPES_FIXTURE
from plone.app.robotframework.testing import REMOTE_LIBRARY_BUNDLE_FIXTURE
-from plone.app.testing import applyProfile
from plone.app.testing import FunctionalTesting
from plone.app.testing import IntegrationTesting
from plone.app.testing import PloneSandboxLayer
+from plone.app.testing import applyProfile
from plone.restapi.testing import PloneRestApiDXLayer
from plone.testing import z2
-import collective.volto.cookieconsent
-import kitconcept.seo
-import plone.app.caching
-import plone.restapi
-import plone.volto
import redturtle.volto
diff --git a/src/redturtle/volto/tests/test_advancedsearch.py b/src/redturtle/volto/tests/test_advancedsearch.py
index b7ffc95d..aad4f475 100644
--- a/src/redturtle/volto/tests/test_advancedsearch.py
+++ b/src/redturtle/volto/tests/test_advancedsearch.py
@@ -1,16 +1,17 @@
# -*- coding: utf-8 -*-
+import unittest
+
from DateTime import DateTime
from plone import api
-from plone.app.testing import setRoles
from plone.app.testing import SITE_OWNER_NAME
from plone.app.testing import SITE_OWNER_PASSWORD
from plone.app.testing import TEST_USER_ID
+from plone.app.testing import setRoles
from plone.restapi.testing import RelativeSession
-from redturtle.volto.interfaces import IRedTurtleVoltoSettings
-from redturtle.volto.testing import REDTURTLE_VOLTO_API_FUNCTIONAL_TESTING
from transaction import commit
-import unittest
+from redturtle.volto.interfaces import IRedTurtleVoltoSettings
+from redturtle.volto.testing import REDTURTLE_VOLTO_API_FUNCTIONAL_TESTING
class BaseTest(unittest.TestCase):
@@ -166,6 +167,10 @@ def test_search_by_not_handled_index_type_return_standard_order(self):
["f1", "d1", "e1"], [item["@id"].split("/")[-1] for item in result["items"]]
)
+ def test_search_no_query(self):
+ response = self.api_session.post("/@querystring-search")
+ self.assertEqual(response.status_code, 400)
+
def test_search_ignore_non_existent_indexes_and_return_custom_order_if_possible(
self,
):
diff --git a/src/redturtle/volto/tests/test_blocks_searchable_text.py b/src/redturtle/volto/tests/test_blocks_searchable_text.py
index cc70a6a8..5033231d 100644
--- a/src/redturtle/volto/tests/test_blocks_searchable_text.py
+++ b/src/redturtle/volto/tests/test_blocks_searchable_text.py
@@ -1,11 +1,12 @@
# -*- coding: utf-8 -*-
+import unittest
+
from plone import api
-from plone.app.testing import setRoles
from plone.app.testing import TEST_USER_ID
-from redturtle.volto.testing import REDTURTLE_VOLTO_FUNCTIONAL_TESTING
+from plone.app.testing import setRoles
from transaction import commit
-import unittest
+from redturtle.volto.testing import REDTURTLE_VOLTO_FUNCTIONAL_TESTING
class TestBlocksSearchable(unittest.TestCase):
diff --git a/src/redturtle/volto/tests/test_blocks_serializer.py b/src/redturtle/volto/tests/test_blocks_serializer.py
index 17859ee2..0a890c22 100644
--- a/src/redturtle/volto/tests/test_blocks_serializer.py
+++ b/src/redturtle/volto/tests/test_blocks_serializer.py
@@ -1,19 +1,20 @@
# -*- coding: utf-8 -*-
+import os
+import unittest
+
from plone import api
-from plone.app.testing import setRoles
from plone.app.testing import SITE_OWNER_NAME
from plone.app.testing import SITE_OWNER_PASSWORD
from plone.app.testing import TEST_USER_ID
+from plone.app.testing import setRoles
from plone.registry.interfaces import IRegistry
from plone.restapi.interfaces import ISerializeToJsonSummary
from plone.restapi.testing import RelativeSession
-from redturtle.volto.testing import REDTURTLE_VOLTO_API_FUNCTIONAL_TESTING
from transaction import commit
from zope.component import getMultiAdapter
from zope.component import getUtility
-import os
-import unittest
+from redturtle.volto.testing import REDTURTLE_VOLTO_API_FUNCTIONAL_TESTING
class TestBlocksSerializer(unittest.TestCase):
diff --git a/src/redturtle/volto/tests/test_catalog_limit_patches.py b/src/redturtle/volto/tests/test_catalog_limit_patches.py
index 872e3571..1127c5ac 100644
--- a/src/redturtle/volto/tests/test_catalog_limit_patches.py
+++ b/src/redturtle/volto/tests/test_catalog_limit_patches.py
@@ -1,16 +1,17 @@
# -*- coding: utf-8 -*-
+import json
+import unittest
+from urllib.parse import quote
+
from plone import api
-from plone.app.testing import setRoles
from plone.app.testing import SITE_OWNER_NAME
from plone.app.testing import SITE_OWNER_PASSWORD
from plone.app.testing import TEST_USER_ID
+from plone.app.testing import setRoles
from plone.restapi.testing import RelativeSession
-from redturtle.volto.testing import REDTURTLE_VOLTO_API_FUNCTIONAL_TESTING
from transaction import commit
-from urllib.parse import quote
-import json
-import unittest
+from redturtle.volto.testing import REDTURTLE_VOLTO_API_FUNCTIONAL_TESTING
class CatalogLimitPatches(unittest.TestCase):
@@ -25,28 +26,33 @@ def setUp(self):
setRoles(self.portal, TEST_USER_ID, ["Manager"])
for i in range(self.MAX_LIMIT + 1):
- api.content.create(
+ doc = api.content.create(
container=self.portal,
type="Document",
title=f"Document {i}",
)
+ api.content.transition(obj=doc, transition="publish")
commit()
self.api_session = RelativeSession(self.portal_url)
self.api_session.headers.update({"Accept": "application/json"})
self.api_session.auth = (SITE_OWNER_NAME, SITE_OWNER_PASSWORD)
+ self.api_session_anon = RelativeSession(self.portal_url)
+ self.api_session_anon.headers.update({"Accept": "application/json"})
+
def tearDown(self):
self.api_session.close()
+ self.api_session_anon.close()
- def test_search_b_size_default_to_500(self):
+ def test_search_b_size_not_limited_for_auth(self):
response = self.api_session.get(
"/@search", params={"portal_type": "Document", "b_size": 1000}
)
result = response.json()
- self.assertEqual(len(result["items"]), self.MAX_LIMIT)
+ self.assertEqual(len(result["items"]), self.MAX_LIMIT + 1)
- def test_querystringsearch_post_default_limit_500(self):
+ def test_querystringsearch_post_not_limited_for_auth(self):
response = self.api_session.post(
"/@querystring-search",
json={
@@ -62,10 +68,56 @@ def test_querystringsearch_post_default_limit_500(self):
},
)
result = response.json()
+ self.assertEqual(len(result["items"]), self.MAX_LIMIT + 1)
+ self.assertEqual(result["items_total"], self.MAX_LIMIT + 1)
+
+ def test_querystringsearch_get_not_limited_for_auth(self):
+ query = {
+ "query": [
+ {
+ "i": "portal_type",
+ "o": "plone.app.querystring.operation.selection.any",
+ "v": ["Document"],
+ }
+ ],
+ "b_size": 2000,
+ "limit": 2000,
+ }
+ response = self.api_session.get(
+ f"/@querystring-search?query={quote(json.dumps(query))}",
+ )
+
+ result = response.json()
+ self.assertEqual(len(result["items"]), self.MAX_LIMIT + 1)
+ self.assertEqual(result["items_total"], self.MAX_LIMIT + 1)
+
+ def test_search_b_size_default_to_500_for_anon(self):
+ response = self.api_session_anon.get(
+ "/@search", params={"portal_type": "Document", "b_size": 1000}
+ )
+ result = response.json()
+ self.assertEqual(len(result["items"]), self.MAX_LIMIT)
+
+ def test_querystringsearch_post_default_limit_500_for_anon(self):
+ response = self.api_session_anon.post(
+ "/@querystring-search",
+ json={
+ "query": [
+ {
+ "i": "portal_type",
+ "o": "plone.app.querystring.operation.selection.is",
+ "v": ["Document"],
+ }
+ ],
+ "limit": 2000,
+ "b_size": 2000,
+ },
+ )
+ result = response.json()
self.assertEqual(len(result["items"]), self.MAX_LIMIT)
self.assertEqual(result["items_total"], self.MAX_LIMIT)
- def test_querystringsearch_get_default_limit_500(self):
+ def test_querystringsearch_get_default_limit_500_for_anon(self):
query = {
"query": [
{
@@ -77,7 +129,7 @@ def test_querystringsearch_get_default_limit_500(self):
"b_size": 2000,
"limit": 2000,
}
- response = self.api_session.get(
+ response = self.api_session_anon.get(
f"/@querystring-search?query={quote(json.dumps(query))}",
)
diff --git a/src/redturtle/volto/tests/test_content_types.py b/src/redturtle/volto/tests/test_content_types.py
index 9d1f7ed1..470195c8 100644
--- a/src/redturtle/volto/tests/test_content_types.py
+++ b/src/redturtle/volto/tests/test_content_types.py
@@ -1,16 +1,17 @@
# -*- coding: utf-8 -*-
"""Setup tests for this package."""
+import unittest
+
from plone import api
-from plone.app.testing import setRoles
from plone.app.testing import SITE_OWNER_NAME
from plone.app.testing import SITE_OWNER_PASSWORD
from plone.app.testing import TEST_USER_ID
+from plone.app.testing import setRoles
from plone.restapi.testing import RelativeSession
+
from redturtle.volto.testing import REDTURTLE_VOLTO_API_FUNCTIONAL_TESTING
from redturtle.volto.testing import REDTURTLE_VOLTO_INTEGRATION_TESTING
-import unittest
-
class TestContentTypes(unittest.TestCase):
layer = REDTURTLE_VOLTO_INTEGRATION_TESTING
diff --git a/src/redturtle/volto/tests/test_controlpanel_api.py b/src/redturtle/volto/tests/test_controlpanel_api.py
index c2eb3704..2c2fcf2a 100644
--- a/src/redturtle/volto/tests/test_controlpanel_api.py
+++ b/src/redturtle/volto/tests/test_controlpanel_api.py
@@ -1,12 +1,13 @@
# -*- coding: utf-8 -*-
-from plone.app.testing import setRoles
+import unittest
+
from plone.app.testing import SITE_OWNER_NAME
from plone.app.testing import SITE_OWNER_PASSWORD
from plone.app.testing import TEST_USER_ID
+from plone.app.testing import setRoles
from plone.restapi.testing import RelativeSession
-from redturtle.volto.testing import REDTURTLE_VOLTO_API_FUNCTIONAL_TESTING
-import unittest
+from redturtle.volto.testing import REDTURTLE_VOLTO_API_FUNCTIONAL_TESTING
class ControlpanelTest(unittest.TestCase):
diff --git a/src/redturtle/volto/tests/test_ct_link.py b/src/redturtle/volto/tests/test_ct_link.py
index 5fb981c1..a199e4c5 100644
--- a/src/redturtle/volto/tests/test_ct_link.py
+++ b/src/redturtle/volto/tests/test_ct_link.py
@@ -1,15 +1,16 @@
# -*- coding: utf-8 -*-
"""Setup tests for this package."""
+import unittest
+
from plone import api
-from plone.app.testing import setRoles
from plone.app.testing import SITE_OWNER_NAME
from plone.app.testing import SITE_OWNER_PASSWORD
from plone.app.testing import TEST_USER_ID
+from plone.app.testing import setRoles
from plone.restapi.testing import RelativeSession
-from redturtle.volto.testing import REDTURTLE_VOLTO_API_FUNCTIONAL_TESTING
from transaction import commit
-import unittest
+from redturtle.volto.testing import REDTURTLE_VOLTO_API_FUNCTIONAL_TESTING
class TestContentTypeLink(unittest.TestCase):
diff --git a/src/redturtle/volto/tests/test_dxfield_deserializer.py b/src/redturtle/volto/tests/test_dxfield_deserializer.py
new file mode 100644
index 00000000..3ada70b8
--- /dev/null
+++ b/src/redturtle/volto/tests/test_dxfield_deserializer.py
@@ -0,0 +1,105 @@
+import unittest
+
+import transaction
+from plone.app.testing import TEST_USER_ID
+from plone.app.testing import TEST_USER_NAME
+from plone.app.testing import login
+from plone.app.testing import setRoles
+from plone.dexterity.utils import iterSchemata
+from plone.restapi.interfaces import IFieldDeserializer
+from Products.CMFCore.utils import getToolByName
+from zope.component import getMultiAdapter
+
+from redturtle.volto.testing import REDTURTLE_VOLTO_API_FUNCTIONAL_TESTING
+
+
+class TestDXFieldDeserializer(unittest.TestCase):
+ layer = REDTURTLE_VOLTO_API_FUNCTIONAL_TESTING
+
+ def setUp(self):
+ self.app = self.layer["app"]
+ self.request = self.layer["request"]
+ self.portal = self.layer["portal"]
+
+ self.wftool = getToolByName(self.portal, "portal_workflow")
+ self.acl_users = getToolByName(self.portal, "acl_users")
+
+ self.acl_users.userFolderAddUser("user1", "secret", ["Manager"], [])
+ self.acl_users.getUser("user1")
+
+ login(self.portal, "user1")
+
+ # folders
+ self.portal.invokeFactory("Folder", id="folder", title="Private Folder")
+ self.portal.folder.invokeFactory(
+ "Folder", id="otherfolder", title="Other Folder"
+ )
+ self.wftool.doActionFor(self.portal.folder.otherfolder, "publish")
+
+ self.portal.invokeFactory("Document", id="doc1", title="Test Document")
+ self.wftool.doActionFor(self.portal.doc1, "publish")
+
+ transaction.commit()
+
+ def deserialize(self, fieldname, value):
+ for schema in iterSchemata(self.portal.doc1):
+ if fieldname in schema:
+ field = schema.get(fieldname)
+ break
+ deserializer = getMultiAdapter(
+ (field, self.portal.doc1, self.request), IFieldDeserializer
+ )
+ return deserializer(value)
+
+ def test_relationlist_deserialization_broke_with_path(self):
+ # documents
+ doc2 = self.portal.folder.otherfolder[
+ self.portal.folder.otherfolder.invokeFactory(
+ "Document", id="doc2", title="Referenceable Document"
+ )
+ ]
+ self.wftool.doActionFor(self.portal.folder.otherfolder.doc2, "publish")
+ doc3 = self.portal.folder.otherfolder[
+ self.portal.folder.otherfolder.invokeFactory(
+ "Document", id="doc3", title="Referenceable Document"
+ )
+ ]
+ self.wftool.doActionFor(self.portal.folder.otherfolder.doc3, "publish")
+
+ setRoles(self.portal, TEST_USER_ID, ["Member"])
+ login(self.portal, TEST_USER_NAME)
+
+ try:
+ value = self.deserialize(
+ "relatedItems",
+ [str(doc2.absolute_url_path()), str(doc3.absolute_url_path())],
+ )
+ except ValueError:
+ value = None
+
+ self.assertIsNone(value)
+
+ def test_relationlist_deserialization_correct_with_uid(self):
+ # documents
+ doc2 = self.portal.folder.otherfolder[
+ self.portal.folder.otherfolder.invokeFactory(
+ "Document", id="doc2", title="Referenceable Document"
+ )
+ ]
+ self.wftool.doActionFor(self.portal.folder.otherfolder.doc2, "publish")
+ doc3 = self.portal.folder.otherfolder[
+ self.portal.folder.otherfolder.invokeFactory(
+ "Document", id="doc3", title="Referenceable Document"
+ )
+ ]
+ self.wftool.doActionFor(self.portal.folder.otherfolder.doc3, "publish")
+
+ setRoles(self.portal, TEST_USER_ID, ["Member"])
+ login(self.portal, TEST_USER_NAME)
+
+ value = self.deserialize(
+ "relatedItems",
+ [str(doc2.UID()), str(doc3.UID())],
+ )
+
+ self.assertTrue(value)
diff --git a/src/redturtle/volto/tests/test_monkeypatches.py b/src/redturtle/volto/tests/test_monkeypatches.py
index a10ade03..4cf0264c 100644
--- a/src/redturtle/volto/tests/test_monkeypatches.py
+++ b/src/redturtle/volto/tests/test_monkeypatches.py
@@ -1,11 +1,12 @@
# -*- coding: utf-8 -*-
+import unittest
+
from plone import api
-from plone.app.testing import setRoles
from plone.app.testing import TEST_USER_ID
+from plone.app.testing import setRoles
from Products.CMFPlone.interfaces import ISelectableConstrainTypes
-from redturtle.volto.testing import REDTURTLE_VOLTO_FUNCTIONAL_TESTING
-import unittest
+from redturtle.volto.testing import REDTURTLE_VOLTO_FUNCTIONAL_TESTING
class TestRespectLocallyAllowedTypes(unittest.TestCase):
diff --git a/src/redturtle/volto/tests/test_publication_fields_fixes.py b/src/redturtle/volto/tests/test_publication_fields_fixes.py
index 751ce255..40390024 100644
--- a/src/redturtle/volto/tests/test_publication_fields_fixes.py
+++ b/src/redturtle/volto/tests/test_publication_fields_fixes.py
@@ -1,17 +1,18 @@
# -*- coding: utf-8 -*-
+import os
+import unittest
+
from DateTime import DateTime
-from plone.app.testing import setRoles
from plone.app.testing import SITE_OWNER_NAME
from plone.app.testing import SITE_OWNER_PASSWORD
from plone.app.testing import TEST_USER_ID
+from plone.app.testing import setRoles
from plone.registry.interfaces import IRegistry
from plone.restapi.testing import RelativeSession
-from redturtle.volto.testing import REDTURTLE_VOLTO_API_FUNCTIONAL_TESTING
from transaction import commit
from zope.component import getUtility
-import os
-import unittest
+from redturtle.volto.testing import REDTURTLE_VOLTO_API_FUNCTIONAL_TESTING
class TestPublicationFieldsFixes(unittest.TestCase):
diff --git a/src/redturtle/volto/tests/test_resolveuid_re_patch.py b/src/redturtle/volto/tests/test_resolveuid_re_patch.py
index ec916706..bf9bb417 100644
--- a/src/redturtle/volto/tests/test_resolveuid_re_patch.py
+++ b/src/redturtle/volto/tests/test_resolveuid_re_patch.py
@@ -1,11 +1,12 @@
# -*- coding: utf-8 -*-
+import unittest
+
from plone import api
-from plone.app.testing import setRoles
from plone.app.testing import TEST_USER_ID
+from plone.app.testing import setRoles
from plone.restapi.serializer.utils import uid_to_url
-from redturtle.volto.testing import REDTURTLE_VOLTO_FUNCTIONAL_TESTING
-import unittest
+from redturtle.volto.testing import REDTURTLE_VOLTO_FUNCTIONAL_TESTING
class TestRESOLVEUIDREPatch(unittest.TestCase):
diff --git a/src/redturtle/volto/tests/test_service_context_navigation.py b/src/redturtle/volto/tests/test_service_context_navigation.py
index 00fd7c60..a7406d5f 100644
--- a/src/redturtle/volto/tests/test_service_context_navigation.py
+++ b/src/redturtle/volto/tests/test_service_context_navigation.py
@@ -1,14 +1,15 @@
# -*- coding: utf-8 -*-
+import unittest
+
+import transaction
from plone import api
-from plone.app.testing import setRoles
from plone.app.testing import SITE_OWNER_NAME
from plone.app.testing import SITE_OWNER_PASSWORD
from plone.app.testing import TEST_USER_ID
+from plone.app.testing import setRoles
from plone.restapi.testing import RelativeSession
-from redturtle.volto.testing import REDTURTLE_VOLTO_API_FUNCTIONAL_TESTING
-import transaction
-import unittest
+from redturtle.volto.testing import REDTURTLE_VOLTO_API_FUNCTIONAL_TESTING
class TestServicesContextNavigation(unittest.TestCase):
diff --git a/src/redturtle/volto/tests/test_service_sitemap.py b/src/redturtle/volto/tests/test_service_sitemap.py
index 0171dc29..4810a6d2 100644
--- a/src/redturtle/volto/tests/test_service_sitemap.py
+++ b/src/redturtle/volto/tests/test_service_sitemap.py
@@ -1,14 +1,15 @@
# -*- coding: utf-8 -*-
+import unittest
+
from plone import api
-from plone.app.testing import setRoles
from plone.app.testing import SITE_OWNER_NAME
from plone.app.testing import SITE_OWNER_PASSWORD
from plone.app.testing import TEST_USER_ID
+from plone.app.testing import setRoles
from plone.restapi.testing import RelativeSession
-from redturtle.volto.testing import REDTURTLE_VOLTO_API_FUNCTIONAL_TESTING
from transaction import commit
-import unittest
+from redturtle.volto.testing import REDTURTLE_VOLTO_API_FUNCTIONAL_TESTING
class TestServicesSitemap(unittest.TestCase):
diff --git a/src/redturtle/volto/tests/test_setup.py b/src/redturtle/volto/tests/test_setup.py
index 3db707e9..8ebb38ef 100644
--- a/src/redturtle/volto/tests/test_setup.py
+++ b/src/redturtle/volto/tests/test_setup.py
@@ -1,12 +1,12 @@
# -*- coding: utf-8 -*-
"""Setup tests for this package."""
+import unittest
+
from plone import api
-from plone.app.testing import setRoles
from plone.app.testing import TEST_USER_ID
-from redturtle.volto.testing import REDTURTLE_VOLTO_INTEGRATION_TESTING # noqa: E501
-
-import unittest
+from plone.app.testing import setRoles
+from redturtle.volto.testing import REDTURTLE_VOLTO_INTEGRATION_TESTING # noqa: E501
try:
from Products.CMFPlone.utils import get_installer
@@ -37,6 +37,7 @@ def test_product_installed(self):
def test_browserlayer(self):
"""Test that IRedturtleVoltoLayer is registered."""
from plone.browserlayer import utils
+
from redturtle.volto.interfaces import IRedturtleVoltoLayer
self.assertIn(IRedturtleVoltoLayer, utils.registered_layers())
@@ -79,6 +80,7 @@ def test_product_uninstalled(self):
def test_browserlayer_removed(self):
"""Test that IRedturtleVoltoLayer is removed."""
from plone.browserlayer import utils
+
from redturtle.volto.interfaces import IRedturtleVoltoLayer
self.assertNotIn(IRedturtleVoltoLayer, utils.registered_layers())
diff --git a/src/redturtle/volto/tests/test_site_search.py b/src/redturtle/volto/tests/test_site_search.py
index b5f5b718..f06d5f57 100644
--- a/src/redturtle/volto/tests/test_site_search.py
+++ b/src/redturtle/volto/tests/test_site_search.py
@@ -1,17 +1,18 @@
# -*- coding: utf-8 -*-
+import unittest
+
from plone import api
-from plone.app.testing import setRoles
from plone.app.testing import SITE_OWNER_NAME
from plone.app.testing import SITE_OWNER_PASSWORD
from plone.app.testing import TEST_USER_ID
+from plone.app.testing import setRoles
from plone.registry.interfaces import IRegistry
from plone.restapi.testing import RelativeSession
from Products.CMFPlone.interfaces import ISearchSchema
-from redturtle.volto.testing import REDTURTLE_VOLTO_API_FUNCTIONAL_TESTING
from transaction import commit
from zope.component import getUtility
-import unittest
+from redturtle.volto.testing import REDTURTLE_VOLTO_API_FUNCTIONAL_TESTING
class SiteSearchTest(unittest.TestCase):
diff --git a/src/redturtle/volto/tests/test_summary_customization.py b/src/redturtle/volto/tests/test_summary_customization.py
index 535056a0..ee96e480 100644
--- a/src/redturtle/volto/tests/test_summary_customization.py
+++ b/src/redturtle/volto/tests/test_summary_customization.py
@@ -1,16 +1,17 @@
# -*- coding: utf-8 -*-
+import os
+import unittest
+
+import transaction
from plone import api
-from plone.app.testing import setRoles
from plone.app.testing import SITE_OWNER_NAME
from plone.app.testing import SITE_OWNER_PASSWORD
from plone.app.testing import TEST_USER_ID
+from plone.app.testing import setRoles
from plone.namedfile.file import NamedBlobImage
from plone.restapi.testing import RelativeSession
-from redturtle.volto.testing import REDTURTLE_VOLTO_API_FUNCTIONAL_TESTING
-import os
-import transaction
-import unittest
+from redturtle.volto.testing import REDTURTLE_VOLTO_API_FUNCTIONAL_TESTING
class TestSummaryCustomization(unittest.TestCase):
diff --git a/src/redturtle/volto/tests/test_vocabulary_endpoint.py b/src/redturtle/volto/tests/test_vocabulary_endpoint.py
index 59634e88..a7825991 100644
--- a/src/redturtle/volto/tests/test_vocabulary_endpoint.py
+++ b/src/redturtle/volto/tests/test_vocabulary_endpoint.py
@@ -1,12 +1,13 @@
# -*- coding: utf-8 -*-
+import unittest
+
+import transaction
from plone import api
-from plone.app.testing import setRoles
from plone.app.testing import TEST_USER_ID
+from plone.app.testing import setRoles
from plone.restapi.testing import RelativeSession
-from redturtle.volto.testing import REDTURTLE_VOLTO_API_FUNCTIONAL_TESTING
-import transaction
-import unittest
+from redturtle.volto.testing import REDTURTLE_VOLTO_API_FUNCTIONAL_TESTING
class TestVocabularyEndpoint(unittest.TestCase):
diff --git a/src/redturtle/volto/types/adapters.py b/src/redturtle/volto/types/adapters.py
index 286b6a53..d7e53883 100644
--- a/src/redturtle/volto/types/adapters.py
+++ b/src/redturtle/volto/types/adapters.py
@@ -2,14 +2,15 @@
"""JsonSchema providers."""
from plone.restapi.types.adapters import TextLineJsonSchemaProvider as Base
from plone.restapi.types.interfaces import IJsonSchemaProvider
-from redturtle.volto import _
-from redturtle.volto.interfaces import IRedturtleVoltoLayer
from zope.component import adapter
from zope.i18n import translate
-from zope.interface import implementer
from zope.interface import Interface
+from zope.interface import implementer
from zope.schema.interfaces import ITextLine
+from redturtle.volto import _
+from redturtle.volto.interfaces import IRedturtleVoltoLayer
+
@adapter(ITextLine, Interface, IRedturtleVoltoLayer)
@implementer(IJsonSchemaProvider)
diff --git a/src/redturtle/volto/upgrades.py b/src/redturtle/volto/upgrades.py
index 0858f351..726e832c 100644
--- a/src/redturtle/volto/upgrades.py
+++ b/src/redturtle/volto/upgrades.py
@@ -1,17 +1,16 @@
# -*- coding: utf-8 -*-
-from Acquisition import aq_base
+import json
+import logging
from copy import deepcopy
+from uuid import uuid4
+
+from Acquisition import aq_base
from plone import api
from plone.app.upgrade.utils import installOrReinstallProduct
from plone.dexterity.utils import iterSchemata
from plone.restapi.behaviors import IBlocks
-from uuid import uuid4
from zope.schema import getFields
-import json
-import logging
-
-
try:
from collective.volto.blocksfield.field import BlocksField
@@ -464,3 +463,12 @@ def to_4200(context):
logger.info("Add redturtle.volto controlpanel")
update_registry(context)
update_controlpanel(context)
+
+
+def to_4301(context):
+ brains = api.content.find(portal_type="Event")
+ logger.info("Reindexing {} Events".format(len(brains)))
+
+ for brain in brains:
+ event = brain.getObject()
+ event.reindexObject(idxs=["start"])
diff --git a/src/redturtle/volto/upgrades.zcml b/src/redturtle/volto/upgrades.zcml
index cb841fd3..2aa7f684 100644
--- a/src/redturtle/volto/upgrades.zcml
+++ b/src/redturtle/volto/upgrades.zcml
@@ -180,5 +180,22 @@
handler=".upgrades.to_4200"
/>
+
+
+