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" /> + + +