Skip to content

Commit

Permalink
Merge pull request #17 from eea/develop
Browse files Browse the repository at this point in the history
Release: Image scales support in Plone 5 [nileshgulia1 - refs #254889]
  • Loading branch information
avoinea authored Aug 30, 2023
2 parents 38fe7b8 + 4fa4799 commit 96147f5
Show file tree
Hide file tree
Showing 13 changed files with 494 additions and 2 deletions.
1 change: 1 addition & 0 deletions Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -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'''
}
},
Expand Down
5 changes: 5 additions & 0 deletions docs/HISTORY.txt
Original file line number Diff line number Diff line change
@@ -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
Expand Down
5 changes: 5 additions & 0 deletions eea/volto/policy/configure.zcml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:zcml="http://namespaces.zope.org/zcml"
xmlns:browser="http://namespaces.zope.org/browser"
xmlns:i18n="http://namespaces.zope.org/i18n"
i18n_domain="eea">
Expand All @@ -14,4 +15,8 @@
<include package=".vocabularies" />
<include package=".upgrades" />

<configure zcml:condition="not-installed Products.CMFPlone.image_scales">
<include package=".image_scales" />
</configure>

</configure>
1 change: 1 addition & 0 deletions eea/volto/policy/image_scales/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
""" Image scales fallback for Plone 5 """
175 changes: 175 additions & 0 deletions eea/volto/policy/image_scales/adapters.py
Original file line number Diff line number Diff line change
@@ -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("/")
16 changes: 16 additions & 0 deletions eea/volto/policy/image_scales/configure.zcml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:zcml="http://namespaces.zope.org/zcml"
>

<!-- indexer -->
<adapter
factory=".indexer.image_scales"
name="image_scales"
/>

<!-- adapters -->
<adapter factory=".adapters.ImageScales" />
<adapter factory=".adapters.ImageFieldScales" />

</configure>
31 changes: 31 additions & 0 deletions eea/volto/policy/image_scales/indexer.py
Original file line number Diff line number Diff line change
@@ -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)
Loading

0 comments on commit 96147f5

Please sign in to comment.