diff --git a/Jenkinsfile b/Jenkinsfile
index d22b2ea..897e4e2 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -121,6 +121,7 @@ pipeline {
*/
"Plone5 & Python3": {
node(label: 'docker') {
+ sh '''docker pull eeacms/plone-test:5-python3'''
sh '''docker run -i --rm --name="$BUILD_TAG-python3" -e GIT_BRANCH="$BRANCH_NAME" -e ADDONS="$GIT_NAME" -e DEVELOP="src/$GIT_NAME" -e GIT_CHANGE_ID="$CHANGE_ID" eeacms/plone-test:5-python3 -v -vv -s $GIT_NAME'''
}
},
diff --git a/docs/HISTORY.txt b/docs/HISTORY.txt
index ea73ded..aa8ccff 100644
--- a/docs/HISTORY.txt
+++ b/docs/HISTORY.txt
@@ -1,6 +1,11 @@
Changelog
=========
+3.0 - (2023-08-30)
+---------------------------
+* Feature: Add image_scales to catalog and update list of scales to registry
+ [nileshgulia1 - refs #254889]
+
2.3 - (2023-07-31)
---------------------------
* Docs: Update sonarqube tags
diff --git a/eea/volto/policy/configure.zcml b/eea/volto/policy/configure.zcml
index 2dc0823..126f8b6 100644
--- a/eea/volto/policy/configure.zcml
+++ b/eea/volto/policy/configure.zcml
@@ -1,5 +1,6 @@
@@ -14,4 +15,8 @@
+
+
+
+
diff --git a/eea/volto/policy/image_scales/__init__.py b/eea/volto/policy/image_scales/__init__.py
new file mode 100644
index 0000000..2db3b13
--- /dev/null
+++ b/eea/volto/policy/image_scales/__init__.py
@@ -0,0 +1 @@
+""" Image scales fallback for Plone 5 """
diff --git a/eea/volto/policy/image_scales/adapters.py b/eea/volto/policy/image_scales/adapters.py
new file mode 100644
index 0000000..b05b66c
--- /dev/null
+++ b/eea/volto/policy/image_scales/adapters.py
@@ -0,0 +1,175 @@
+# pylint: disable=ungrouped-imports
+"""
+ImageScales
+"""
+from Acquisition import aq_inner
+from plone.dexterity.interfaces import IDexterityContent
+from plone.dexterity.utils import iterSchemata
+from zope.component import adapter
+from zope.component import queryMultiAdapter
+from zope.interface import implementer
+from zope.interface import Interface
+from zope.schema import getFields
+from plone.namedfile.interfaces import INamedImageField
+from plone.registry.interfaces import IRegistry
+from zope.component import getMultiAdapter
+
+from zope.component import getUtility
+from zope.interface.interfaces import ComponentLookupError
+
+from eea.volto.policy.image_scales.interfaces import (
+ IImageScalesAdapter,
+ IImageScalesFieldAdapter,
+ IImagingSchema,
+)
+
+
+@implementer(IImageScalesAdapter)
+@adapter(IDexterityContent, Interface)
+class ImageScales(object):
+ """
+ Adapter for getting image scales
+ """
+
+ def __init__(self, context, request):
+ self.context = context
+ self.request = request
+
+ def __call__(self):
+ obj = aq_inner(self.context)
+ res = {}
+ for schema in iterSchemata(self.context):
+ for name, field in getFields(schema).items():
+ # serialize the field
+ serializer = queryMultiAdapter(
+ (field, obj, self.request), IImageScalesFieldAdapter
+ )
+ if serializer:
+ scales = serializer()
+ if scales:
+ res[name] = scales
+ return res
+
+
+def _split_scale_info(allowed_size):
+ """
+ get desired attr(name,width,height) from scale names
+ """
+ name, dims = allowed_size.split(" ")
+ width, height = list(map(int, dims.split(":")))
+ return name, width, height
+
+
+def _get_scale_infos():
+ """Returns list of (name, width, height) of the available image scales."""
+ if IImagingSchema is None:
+ return []
+ registry = getUtility(IRegistry)
+ imaging_settings = registry.forInterface(
+ IImagingSchema, prefix="plone", omit=("picture_variants")
+ )
+ allowed_sizes = imaging_settings.allowed_sizes
+ return [_split_scale_info(size) for size in allowed_sizes]
+
+
+@implementer(IImageScalesFieldAdapter)
+@adapter(INamedImageField, IDexterityContent, Interface)
+class ImageFieldScales(object):
+ """
+ Image scale serializer
+ """
+
+ def __init__(self, field, context, request):
+ self.context = context
+ self.request = request
+ self.field = field
+
+ def __call__(self):
+ image = self.field.get(self.context)
+ if not image:
+ return None
+
+ # Get the @@images view once and store it, so all methods can use it.
+ try:
+ self.images_view = getMultiAdapter(
+ (self.context, self.request), name="images"
+ )
+ except ComponentLookupError:
+ # Seen in plone.app.caching.tests.test_profile_with_caching_proxy.
+ # If we cannot find the images view, there is nothing for us to do.
+ return None
+ width, height = image.getImageSize()
+ url = self.get_original_image_url(self.field.__name__, width, height)
+ scales = self.get_scales(self.field, width, height)
+
+ return [
+ {
+ "filename": image.filename,
+ "content-type": image.contentType,
+ "size": image.getSize(),
+ "download": self._scale_view_from_url(url),
+ "width": width,
+ "height": height,
+ "scales": scales,
+ }
+ ]
+
+ def get_scales(self, field, width, height):
+ """Get a dictionary of available scales for a particular image field,
+ with the actual dimensions (aspect ratio of the original image).
+ """
+ scales = {}
+
+ for name, actual_width, actual_height in _get_scale_infos():
+ if actual_width > width:
+ # The width of the scale is larger than the original width.
+ # Scaling would simply return the original (or perhaps a copy
+ # with the same size). We do not need this scale.
+ # If we *do* want this, we should call the scale method with
+ # mode="cover", so it scales up.
+ continue
+
+ # Get the scale info without actually generating the scale,
+ # nor any old-style HiDPI scales.
+ scale = self.images_view.scale(
+ field.__name__,
+ width=actual_width,
+ height=actual_height,
+ )
+ if scale is None:
+ # If we cannot get a scale, it is probably a corrupt image.
+ continue
+
+ url = scale.url
+ actual_width = scale.width
+ actual_height = scale.height
+
+ scales[name] = {
+ "download": self._scale_view_from_url(url),
+ "width": actual_width,
+ "height": actual_height,
+ }
+
+ return scales
+
+ def get_original_image_url(self, fieldname, width, height):
+ """
+ get image url from scale
+ """
+ scale = self.images_view.scale(
+ fieldname,
+ width=width,
+ height=height,
+ )
+ # Corrupt images may not have a scale.
+ return scale.url if scale else None
+
+ def _scale_view_from_url(self, url):
+ """
+ flatten to scale url
+ """
+ # The "download" information for scales is the path to
+ # "@@images/foo-scale" only.
+ # The full URL to the scale is rendered by the scaling adapter at
+ # runtime to make sure they are correct in virtual hostings.
+ return url.replace(self.context.absolute_url(), "").lstrip("/")
diff --git a/eea/volto/policy/image_scales/configure.zcml b/eea/volto/policy/image_scales/configure.zcml
new file mode 100644
index 0000000..01ea9b2
--- /dev/null
+++ b/eea/volto/policy/image_scales/configure.zcml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/eea/volto/policy/image_scales/indexer.py b/eea/volto/policy/image_scales/indexer.py
new file mode 100644
index 0000000..08b10fe
--- /dev/null
+++ b/eea/volto/policy/image_scales/indexer.py
@@ -0,0 +1,31 @@
+# pylint: disable=ungrouped-imports
+"""
+Indexer
+"""
+
+from persistent.dict import PersistentDict
+from plone.dexterity.interfaces import IDexterityContent
+from plone.indexer.decorator import indexer
+from zope.component import queryMultiAdapter
+from zope.globalrequest import getRequest
+
+from eea.volto.policy.image_scales.interfaces import IImageScalesAdapter
+
+
+@indexer(IDexterityContent)
+def image_scales(obj):
+ """
+ Indexer used to store in metadata the image scales of the object.
+ """
+ adapter = queryMultiAdapter((obj, getRequest()), IImageScalesAdapter)
+ if not adapter:
+ # Raising an AttributeError does the right thing,
+ # making sure nothing is saved in the catalog.
+ raise AttributeError
+ try:
+ scales = adapter()
+ except TypeError:
+ scales = {}
+ if not scales:
+ raise AttributeError
+ return PersistentDict(scales)
diff --git a/eea/volto/policy/image_scales/interfaces.py b/eea/volto/policy/image_scales/interfaces.py
new file mode 100644
index 0000000..678f898
--- /dev/null
+++ b/eea/volto/policy/image_scales/interfaces.py
@@ -0,0 +1,219 @@
+# -*- coding: utf-8 -*-
+"""Module where all interfaces, events and exceptions live."""
+import json
+from plone import schema
+from zope.interface import Interface
+from zope.schema.vocabulary import SimpleTerm
+from zope.schema.vocabulary import SimpleVocabulary
+from eea.volto.policy import EEAMessageFactory as _
+
+
+class IImageScalesAdapter(Interface):
+ """Return a list of image scales for the given context."""
+
+ def __init__(self, context, request):
+ """Adapts context and the request."""
+
+ def __call__(self):
+ """Call IImageScalesFieldAdapter on all fields."""
+
+
+class IImageScalesFieldAdapter(Interface):
+ """Adapter from field to image_scales.
+
+ This is called by an IImageScalesAdapter.
+ Default expectation is that there will be adapters for image fields
+ and not for others. But adapters for text fields or relation fields
+ are imaginable.
+ """
+
+ def __init__(self, field, context, request):
+ """Adapts field, context and request."""
+
+ def __call__(self):
+ """Returns JSON compatible python data."""
+
+
+class IImagingSchema(Interface):
+ """
+ Schema for Images Controlpanel
+ """
+
+ allowed_sizes = schema.List(
+ title=_("Allowed image sizes"),
+ description=_(
+ "Specify all allowed maximum image dimensions, one per line. The "
+ "required format is <name> <width>:<height>."
+ ),
+ value_type=schema.TextLine(),
+ default=[
+ "huge 1600:65536",
+ "great 1200:65536",
+ "larger 1000:65536",
+ "large 800:65536",
+ "teaser 600:65536",
+ "preview 400:65536",
+ "mini 200:65536",
+ "thumb 128:128",
+ "tile 64:64",
+ "icon 32:32",
+ "listing 16:16",
+ ],
+ missing_value=[],
+ required=False,
+ )
+
+ quality = schema.Int(
+ title=_("Scaled image quality"),
+ description=_(
+ "A value for the quality of scaled images, from 1 "
+ "(lowest) to 95 (highest). A value of 0 will mean "
+ "plone.scaling's default will be used, which is "
+ "currently 88."
+ ),
+ min=0,
+ max=95,
+ default=88,
+ )
+
+ highpixeldensity_scales = schema.Choice(
+ title=_("High pixel density mode"),
+ description=_(""),
+ default="disabled",
+ vocabulary=SimpleVocabulary(
+ [
+ SimpleTerm("disabled", "disabled", "Disabled"),
+ SimpleTerm("2x", "2x", "Enabled (2x)"),
+ SimpleTerm("3x", "3x", "Enabled (2x, 3x)"),
+ ]
+ ),
+ )
+
+ quality_2x = schema.Int(
+ title=_("Image quality at 2x"),
+ description=_(
+ "A value for the quality of 2x high pixel density images, from 1 "
+ "(lowest) to 95 (highest). A value of 0 will mean "
+ "plone.scaling's default will be used, which is "
+ "currently 62."
+ ),
+ min=0,
+ max=95,
+ default=62,
+ )
+
+ quality_3x = schema.Int(
+ title=_("Image quality at 3x"),
+ description=_(
+ "A value for the quality of 3x high pixel density images, from 1 "
+ "(lowest) to 95 (highest). A value of 0 will mean "
+ "plone.scaling's default will be used, which is "
+ "currently 51."
+ ),
+ min=0,
+ max=95,
+ default=51,
+ )
+
+ picture_variants = schema.JSONField(
+ title=_("Picture variants"),
+ description=_(
+ "Enter a JSON-formatted picture variants configuration."
+ ),
+ schema=json.dumps(
+ {
+ "title": "Image srcset definition",
+ "type": "object",
+ "additionalProperties": {"$ref": "#/$defs/srcset"},
+ "$defs": {
+ "srcset": {
+ "type": "object",
+ "properties": {
+ "title": {
+ "type": "string",
+ },
+ "preview": {
+ "type": "string",
+ },
+ "hideInEditor": {
+ "type": "boolean",
+ },
+ "sourceset": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "scale": {
+ "type": "string",
+ },
+ "media": {
+ "type": "string",
+ },
+ "additionalScales": {
+ "type": "array",
+ },
+ },
+ "additionalProperties": False,
+ "required": ["scale"],
+ },
+ },
+ },
+ "additionalProperties": False,
+ "required": ["title", "sourceset"],
+ },
+ },
+ }
+ ),
+ default={
+ "large": {
+ "title": "Large",
+ "sourceset": [
+ {
+ "scale": "larger",
+ "additionalScales": [
+ "preview",
+ "teaser",
+ "large",
+ "great",
+ "huge",
+ ],
+ },
+ ],
+ },
+ "medium": {
+ "title": "Medium",
+ "sourceset": [
+ {
+ "scale": "teaser",
+ "additionalScales": [
+ "preview",
+ "large",
+ "larger",
+ "great",
+ ],
+ },
+ ],
+ },
+ "small": {
+ "title": "Small",
+ "sourceset": [
+ {
+ "scale": "preview",
+ "additionalScales": ["large", "larger"],
+ },
+ ],
+ },
+ },
+ required=True,
+ )
+
+ image_captioning = schema.Bool(
+ title=_("image_captioning_title", "Enable image captioning"),
+ description=_(
+ "image_captioning_description",
+ "Enable automatic image captioning for images set in the richtext"
+ "editor based on the description of images.",
+ ),
+ default=True,
+ required=False,
+ )
diff --git a/eea/volto/policy/profiles/default/catalog.xml b/eea/volto/policy/profiles/default/catalog.xml
new file mode 100644
index 0000000..70d8232
--- /dev/null
+++ b/eea/volto/policy/profiles/default/catalog.xml
@@ -0,0 +1,4 @@
+
+
diff --git a/eea/volto/policy/profiles/default/metadata.xml b/eea/volto/policy/profiles/default/metadata.xml
index 38ddfac..e4ba0c6 100644
--- a/eea/volto/policy/profiles/default/metadata.xml
+++ b/eea/volto/policy/profiles/default/metadata.xml
@@ -1,6 +1,6 @@
- 2.0
+ 3.0
profile-plone.volto:default
diff --git a/eea/volto/policy/profiles/default/registry.xml b/eea/volto/policy/profiles/default/registry.xml
new file mode 100644
index 0000000..1b10e2c
--- /dev/null
+++ b/eea/volto/policy/profiles/default/registry.xml
@@ -0,0 +1,23 @@
+
+
+
+
+ icon 32:32
+ tile 64:64
+ thumb 128:128
+ mini 200:65536
+ preview 400:65536
+ teaser 600:65536
+ large 800:65536
+ larger 1000:65536
+ great 1200:65536
+ tiny 24:24
+ small 48:48
+ medium 60:60
+ big 80:80
+ huge 1920:65536
+
+
+
diff --git a/eea/volto/policy/upgrades/configure.zcml b/eea/volto/policy/upgrades/configure.zcml
index dfd5e54..f8a59ea 100644
--- a/eea/volto/policy/upgrades/configure.zcml
+++ b/eea/volto/policy/upgrades/configure.zcml
@@ -27,4 +27,16 @@
+
+
+
+
+
+
diff --git a/eea/volto/policy/version.txt b/eea/volto/policy/version.txt
index bb576db..9f55b2c 100644
--- a/eea/volto/policy/version.txt
+++ b/eea/volto/policy/version.txt
@@ -1 +1 @@
-2.3
+3.0