Skip to content

Commit

Permalink
Merge pull request #49 from plone/preview-image-link
Browse files Browse the repository at this point in the history
Added preview image link.
  • Loading branch information
davisagli authored Aug 12, 2022
2 parents 666f13d + 6af88e8 commit 431d036
Show file tree
Hide file tree
Showing 10 changed files with 155 additions and 14 deletions.
1 change: 1 addition & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ Bug fixes:
- Fix deprecated import of isDefaultPage
[pbauer]


4.0.0a3 (2022-02-04)
--------------------

Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ black: bin/black ## Format codebase
isort: bin/isort ## Format imports in the codebase
bin/isort $(CHECK_PATH)

.PHONY: zpretty
zpretty: bin/zpretty ## Format xml and zcml with zpretty
find "${PACKAGE_PATH}" -name '*.xml' | xargs bin/zpretty -x -i
find "${PACKAGE_PATH}" -name '*.zcml' | xargs bin/zpretty -z -i
Expand Down
8 changes: 5 additions & 3 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -173,13 +173,13 @@ Plone). This will be fixed in core in upcoming sprints.
Preview Image Behavior
----------------------

The preview image behavior makes content types provide a preview_image field that can store a preview image that Volto views can pick up.
The preview image behavior makes content types provide a ``preview_image`` field that can store a preview image that Volto views can pick up.
This is especially userful for listings (e.g. listing block customizations) and teaser elements (e.g. teaser blocks such as [volto-blocks-grid](https://github.com/kitconcept/volto-blocks-grid)).

The "volto.preview_image behavior can be enabled in the generic setup XML definition of a content type (e.g. "/profiles/default/types/MyContentType.xml")::
The ``volto.preview_image`` behavior can be enabled in the generic setup XML definition of a content type (e.g. ``/profiles/default/types/MyContentType.xml``)::

<?xml version="1.0" encoding="UTF-8" ?>
<object i18n:domain="fzj.internet" meta_type="Dexterity FTI" name="MyContentType"
<object i18n:domain="my.project" meta_type="Dexterity FTI" name="MyContentType"
xmlns:i18n="http://xml.zope.org/namespaces/i18n">

...
Expand All @@ -192,6 +192,8 @@ The "volto.preview_image behavior can be enabled in the generic setup XML defini
...
</object>

There is also another variation of the preview image behavior called ``volto.preview_image_link``.
This one stores preview images using a relation to an Image content type, rather than in an image field. This might be preferable if many content items use the same preview image.

Navigation Title Behavior
-------------------------
Expand Down
2 changes: 2 additions & 0 deletions news/49.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- Added preview image link behavior (Plone 6+ only)
[robgietema]
11 changes: 11 additions & 0 deletions src/plone/volto/behaviors/configure.zcml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:plone="http://namespaces.plone.org/plone"
xmlns:zcml="http://namespaces.zope.org/zcml"
i18n_domain="plone.volto"
>

Expand All @@ -16,6 +17,16 @@
provides=".preview.IPreview"
/>

<configure zcml:condition="have plone-60">
<plone:behavior
name="volto.preview_image_link"
title="Preview Image Link"
description="Preview image for listings based on links"
provides=".preview_link.IPreviewLink"
/>
<adapter factory=".preview_link.PreviewImageScalesFieldAdapter" />
</configure>

<plone:behavior
name="volto.navtitle"
title="Navigation title"
Expand Down
71 changes: 71 additions & 0 deletions src/plone/volto/behaviors/preview_link.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# -*- coding: utf-8 -*-
from plone import api
from plone.app.z3cform.widget import RelatedItemsFieldWidget
from plone.autoform import directives
from plone.autoform.interfaces import IFormFieldProvider
from plone.base.interfaces import IImageScalesFieldAdapter
from plone.rfc822.interfaces import IPrimaryFieldInfo
from plone.supermodel import model
from plone.volto import _
from z3c.form.util import getSpecification
from z3c.relationfield.schema import RelationChoice
from zope.component import adapter
from zope.component import queryMultiAdapter
from zope.interface import implementer
from zope.interface import Interface
from zope.interface import provider
from zope.schema import TextLine


@provider(IFormFieldProvider)
class IPreviewLink(model.Schema):

preview_image_link = RelationChoice(
title=_("label_previewimage", default="Preview image"),
description=_(
"help_previewimage",
default="Select an image that will be used in listing and teaser blocks.",
),
vocabulary="plone.app.vocabularies.Catalog",
required=False,
)

directives.widget(
"preview_image_link",
RelatedItemsFieldWidget,
frontendOptions={
"widget": "object_browser",
"widgetProps": {"mode": "image", "return": "single"},
},
)

preview_caption_link = TextLine(
title=_("Preview image caption"), description="", required=False
)


@adapter(getSpecification(IPreviewLink["preview_image_link"]), Interface, Interface)
@implementer(IImageScalesFieldAdapter)
class PreviewImageScalesFieldAdapter:
"""Get the image_scales for the preview_image_link field"""

def __init__(self, field, context, request):
self.field = field
self.context = context
self.request = request

def __call__(self):
value = self.field.get(self.context)
linked_image = value.to_object
primary_field = IPrimaryFieldInfo(linked_image).field
serializer = queryMultiAdapter(
(primary_field, linked_image, self.request), IImageScalesFieldAdapter
)
if serializer is not None:
values = serializer()
if values:
portal_url = api.portal.get().absolute_url()
base_path = linked_image.absolute_url().replace(portal_url, "")
for value in values:
value["base_path"] = base_path
return values
4 changes: 3 additions & 1 deletion src/plone/volto/indexers.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def hasPreviewImage(obj):
Indexer for knowing in a catalog search if a content with the IPreview behavior has
a preview_image
"""
if obj.aq_base.preview_image:
if obj.aq_base.preview_image or obj.aq_base.preview_image_link:
return True
return False

Expand All @@ -23,6 +23,8 @@ def image_field_indexer(obj):
image_field = ""
if getattr(base_obj, "preview_image", False):
image_field = "preview_image"
elif getattr(base_obj, "preview_image_link", False):
image_field = "preview_image_link"
elif getattr(base_obj, "image", False):
image_field = "image"
return image_field
10 changes: 10 additions & 0 deletions src/plone/volto/testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@
import plone.volto.coresandbox


try:
from Products.CMFPlone.factory import PLONE60MARKER

PLONE60MARKER # pyflakes
except ImportError:
PLONE_6 = False
else:
PLONE_6 = True


class PloneVoltoCoreLayer(PloneSandboxLayer):

defaultBases = (PLONE_APP_CONTENTTYPES_FIXTURE,)
Expand Down
11 changes: 1 addition & 10 deletions src/plone/volto/tests/test_migrate_to_volto.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from plone.volto.content import FolderishDocument
from plone.volto.content import FolderishEvent
from plone.volto.content import FolderishNewsItem
from plone.volto.testing import PLONE_6
from plone.volto.testing import PLONE_VOLTO_MIGRATION_FUNCTIONAL_TESTING
from Products.CMFPlone.utils import get_installer

Expand All @@ -14,16 +15,6 @@
import unittest


try:
from Products.CMFPlone.factory import PLONE60MARKER

PLONE60MARKER # pyflakes
except ImportError:
PLONE_6 = False
else:
PLONE_6 = True


@unittest.skipIf(
not PLONE_6,
"This test is only intended to run for Plone 6",
Expand Down
50 changes: 50 additions & 0 deletions src/plone/volto/tests/test_preview_link_behavior.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# -*- coding: utf-8 -*-

from plone.namedfile.file import NamedBlobImage
from plone.restapi.interfaces import ISerializeToJsonSummary
from plone.volto.testing import PLONE_6
from plone.volto.testing import PLONE_VOLTO_CORE_INTEGRATION_TESTING
from z3c.form.interfaces import IDataManager
from zope.component import getMultiAdapter

import unittest


TEST_GIF = (
b"GIF89a\x01\x00\x01\x00\x00\xff\x00,\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x00;"
)


@unittest.skipIf(
not PLONE_6,
"This test is only intended to run for Plone 6",
)
class TestPreviewLinkBehavior(unittest.TestCase):
layer = PLONE_VOLTO_CORE_INTEGRATION_TESTING

def setUp(self):
from plone.volto.behaviors.preview_link import IPreviewLink

self.app = self.layer["app"]
self.portal = self.layer["portal"]
self.request = self.layer["request"]
self.catalog = self.portal.portal_catalog

fti = self.portal.portal_types.Document
fti.behaviors += ("volto.preview_image_link",)

self.doc = self.portal[self.portal.invokeFactory("Document", id="doc1")]
self.image = self.portal[
self.portal.invokeFactory("Image", id="image-1", title="Target image")
]
self.image.image = NamedBlobImage(data=TEST_GIF, filename="test.gif")
dm = getMultiAdapter(
(self.doc, IPreviewLink["preview_image_link"]), IDataManager
)
dm.set(self.image)
self.doc.reindexObject()

def test_image_scales_includes_preview_image_link(self):
brain = self.catalog(UID=self.doc.UID())[0]
summary = getMultiAdapter((brain, self.request), ISerializeToJsonSummary)()
self.assertIn("preview_image_link", summary["image_scales"])

0 comments on commit 431d036

Please sign in to comment.