Skip to content

Commit

Permalink
refacture image_srcset method to be reusable in plone.namedfile
Browse files Browse the repository at this point in the history
  • Loading branch information
MrTango committed Jun 1, 2022
1 parent 17157fd commit ce96bc9
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 101 deletions.
116 changes: 15 additions & 101 deletions plone/outputfilters/filters/image_srcset.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,17 @@
import re

from bs4 import BeautifulSoup
from plone.base.interfaces import IImagingSchema
from plone.outputfilters.interfaces import IFilter
from plone.registry.interfaces import IRegistry
from Products.CMFPlone.utils import safe_nativestring
from zope.component import getUtility
from zope.interface import implementer
from plone.outputfilters.utils import Img2PictureTag

logger = logging.getLogger("plone.outputfilter.image_srcset")


@implementer(IFilter)
class ImageSrcsetFilter(object):
"""Converts img/figure tags with a data-srcset attribute into srcset definition.
<picture>
<source media="(max-width:768px) and (orientation:portrait)"
srcset="resolveuid/44d84ffd32924bb8b9dbd720f43e761c/@@images/image/teaser" />
<source media="(max-width:768px)"
srcset="resolveuid/44d84ffd32924bb8b9dbd720f43e761c/@@images/image/large" />
<source media="(min-width:992px)"
srcset="resolveuid/44d84ffd32924bb8b9dbd720f43e761c/@@images/image/larger" />
<source media="(min-width:1200px)"
srcset="resolveuid/44d84ffd32924bb8b9dbd720f43e761c/@@images/image/great" />
<source media="(min-width:1400px)"
srcset="resolveuid/44d84ffd32924bb8b9dbd720f43e761c/@@images/image/huge" />
<img src="resolveuid/44d84ffd32924bb8b9dbd720f43e761c/@@images/image/huge" />
</picture>
"""Converts img tags with a data-srcset attribute into picture tag with srcset definition.
"""

order = 700
Expand All @@ -49,36 +34,8 @@ def __init__(self, context=None, request=None):
self.current_status = None
self.context = context
self.request = request
self.img2picturetag = Img2PictureTag()

@property
def allowed_scales(self):
registry = getUtility(IRegistry)
settings = registry.forInterface(IImagingSchema, prefix="plone", check=False)
return settings.allowed_sizes

@property
def image_srcsets(self):
registry = getUtility(IRegistry)
settings = registry.forInterface(IImagingSchema, prefix="plone", check=False)
return settings.image_srcsets

def get_scale_name(self, scale_line):
parts = scale_line.split(" ")
return parts and parts[0] or ""

def get_scale_width(self, scale):
""" get width from allowed_scales line
large 800:65536
"""
for s in self.allowed_scales:
parts = s.split(" ")
if not parts:
continue
if parts[0] == scale:
dimentions = parts[1].split(":")
if not dimentions:
continue
return dimentions[0]

def __call__(self, data):
data = re.sub(r"<([^<>\s]+?)\s*/>", self._shorttag_replace, data)
Expand All @@ -88,59 +45,16 @@ def __call__(self, data):
srcset_name = elem.attrs.get("data-srcset", "")
if not srcset_name:
continue
elem.replace_with(self.convert_to_srcset(srcset_name, elem, soup))
return str(soup)

def convert_to_srcset(self, srcset_name, elem, soup):
"""Converts the element to a srcset definition"""
srcset_config = self.image_srcsets.get(srcset_name)
if not srcset_config:
logger.warn(
"Could not find the given srcset_name {0}, leave tag untouched!".format(
srcset_name
srcset_config = self.img2picturetag.image_srcsets.get(srcset_name)
if not srcset_config:
logger.warn(
"Could not find the given srcset_name {0}, leave tag untouched!".format(
srcset_name
)
)
)
return elem
allowed_scales = self.allowed_scales
sourceset = srcset_config.get("sourceset")
if not sourceset:
return elem
src = elem.attrs.get("src")
picture_tag = soup.new_tag("picture")
css_classes = elem.attrs.get("class")
if "captioned" in css_classes:
picture_tag["class"] = "captioned"
for i, source in enumerate(sourceset):
target_scale = source["scale"]
media = source.get("media")

additional_scales = source.get("additionalScales", None)
if additional_scales is None:
additional_scales = [self.get_scale_name(s) for s in allowed_scales if s != target_scale]
source_scales = [target_scale] + additional_scales
source_srcset = []
for scale in source_scales:
scale_url = self.update_src_scale(src=src, scale=scale)
scale_width = self.get_scale_width(scale)
source_srcset.append("{0} {1}w".format(scale_url, scale_width))
source_tag = soup.new_tag(
"source", srcset=",\n".join(source_srcset)
)
if media:
source_tag["media"] = media
picture_tag.append(source_tag)
if i == len(sourceset) - 1:
img_tag = soup.new_tag(
"img", src=self.update_src_scale(src=src, scale=target_scale),
)
for k, attr in elem.attrs.items():
if k in ["src", "srcset"]:
continue
img_tag.attrs[k] = attr
img_tag["loading"] = "lazy"
picture_tag.append(img_tag)
return picture_tag

def update_src_scale(self, src, scale):
parts = src.split("/")
return "/".join(parts[:-1]) + "/{}".format(scale)
continue
sourceset = srcset_config.get("sourceset")
if not sourceset:
continue
elem.replace_with(self.img2picturetag.create_picture_tag(sourceset, elem.attrs))
return str(soup)
88 changes: 88 additions & 0 deletions plone/outputfilters/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import logging

from plone.base.interfaces import IImagingSchema
from plone.registry.interfaces import IRegistry
from zope.component import getUtility
from bs4 import BeautifulSoup

logger = logging.getLogger("plone.outputfilter.image_srcset")


class Img2PictureTag(object):
@property
def allowed_scales(self):
registry = getUtility(IRegistry)
settings = registry.forInterface(IImagingSchema, prefix="plone", check=False)
return settings.allowed_sizes

@property
def image_srcsets(self):
registry = getUtility(IRegistry)
settings = registry.forInterface(IImagingSchema, prefix="plone", check=False)
return settings.image_srcsets

def get_scale_name(self, scale_line):
parts = scale_line.split(" ")
return parts and parts[0] or ""

def get_scale_width(self, scale):
"""get width from allowed_scales line
large 800:65536
"""
for s in self.allowed_scales:
parts = s.split(" ")
if not parts:
continue
if parts[0] == scale:
dimentions = parts[1].split(":")
if not dimentions:
continue
return dimentions[0]

def create_picture_tag(self, sourceset, attributes):
"""Converts the element to a srcset definition"""
soup = BeautifulSoup("", "html.parser")
allowed_scales = self.allowed_scales
src = attributes.get("src")
picture_tag = soup.new_tag("picture")
css_classes = attributes.get("class")
if "captioned" in css_classes:
picture_tag["class"] = "captioned"
for i, source in enumerate(sourceset):
target_scale = source["scale"]
media = source.get("media")

additional_scales = source.get("additionalScales", None)
if additional_scales is None:
additional_scales = [
self.get_scale_name(s) for s in allowed_scales if s != target_scale
]
source_scales = [target_scale] + additional_scales
source_srcset = []
for scale in source_scales:
scale_url = self.update_src_scale(src=src, scale=scale)
scale_width = self.get_scale_width(scale)
source_srcset.append("{0} {1}w".format(scale_url, scale_width))
source_tag = soup.new_tag("source", srcset=",\n".join(source_srcset))
if media:
source_tag["media"] = media
picture_tag.append(source_tag)
if i == len(sourceset) - 1:
img_tag = soup.new_tag(
"img",
src=self.update_src_scale(src=src, scale=target_scale),
)
for k, attr in attributes.items():
if k in ["src", "srcset"]:
continue
img_tag.attrs[k] = attr
img_tag["loading"] = "lazy"
picture_tag.append(img_tag)
return picture_tag

def update_src_scale(self, src, scale):
parts = src.split("/")
if "." in src:
field_name = parts[-1].split("-")[0]
return "/".join(parts[:-1]) + "/{0}/{1}".format(field_name, scale)
return "/".join(parts[:-1]) + "/{}".format(scale)

0 comments on commit ce96bc9

Please sign in to comment.