Skip to content

Commit

Permalink
Image field in rss (#111)
Browse files Browse the repository at this point in the history
* add new image handle of rss item
  • Loading branch information
luca-bellenghi authored Jul 22, 2024
1 parent 6c01150 commit 2c6b6a1
Show file tree
Hide file tree
Showing 14 changed files with 291 additions and 8 deletions.
6 changes: 4 additions & 2 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ Changelog
5.5.1 (unreleased)
------------------

- Nothing changed yet.
- Allow to select which image miniature use in
RSS
[lucabel]


5.5.0 (2024-07-10)
Expand All @@ -14,7 +16,7 @@ Changelog
[eikichi18]
- Add dependency with collective.volto.sitesettings.
[cekk]

5.4.9 (2024-04-22)
------------------

Expand Down
1 change: 0 additions & 1 deletion buildout.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,3 @@ extends =
test_plone60.cfg

[versions]
plone.restapi = 9.7.0
15 changes: 15 additions & 0 deletions src/redturtle/volto/adapters/configure.zcml
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,19 @@
provides="zope.container.interfaces.INameChooser"
for="Products.CMFCore.interfaces.ISiteRoot"
/>

<adapter
factory=".rss.CustomFeedItem"
provides="plone.base.interfaces.syndication.IFeedItem"
for="redturtle.volto.interfaces.ICustomFeedItem
plone.base.interfaces.syndication.IFeed"
zcml:condition="have plone-60"
/>
<adapter
factory=".rss.CustomFeedItem"
provides="Products.CMFPlone.interfaces.syndication.IFeedItem"
for="redturtle.volto.interfaces.ICustomFeedItem
Products.CMFPlone.interfaces.syndication.IFeed"
zcml:condition="not-have plone-60"
/>
</configure>
78 changes: 77 additions & 1 deletion src/redturtle/volto/adapters/rss.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
from DateTime import DateTime
from plone import api
from plone.api.exc import InvalidParameterError
from plone.app.contenttypes.behaviors.leadimage import ILeadImageBehavior
from plone.app.contenttypes.interfaces import IEvent
from plone.dexterity.interfaces import IDexterityContent
from plone.namedfile.interfaces import INamedField
from plone.rfc822.interfaces import IPrimaryFieldInfo
from plone.volto.behaviors.preview import IPreview
from redturtle.volto.interfaces import ICustomFeedItem


try:
Expand All @@ -11,8 +19,76 @@
from zope.component import adapter


@adapter(ICustomFeedItem, IFeed)
class CustomFeedItem(DexterityItem):

def _has_valid_image(self, behavior, field_name):
if not behavior:
return False

field = getattr(behavior, field_name, None)
if not field:
return False

if not hasattr(field, "getSize"):
return False

return field.getSize() > 0

def __init__(self, context, feed):
super().__init__(context, feed)
self.dexterity = IDexterityContent.providedBy(context)
self.img_choice = None
self.file = None
self.field_name = None
self._set_image_field()
if self.file is None:
self._set_primary_field()

def _set_image_field(self):
lead = ILeadImageBehavior(self.context, None)
preview = IPreview(self.context, None)
# if we don't have the record gently fallback on the old behavior
try:
img_choice = api.portal.get_registry_record(
"redturtle.volto.rss_image_choice"
)
except InvalidParameterError:
# seems the default of get_registry_record is not working
img_choice = "image"

if img_choice == "preview_image" and self._has_valid_image(
preview, "preview_image"
):
self.file = preview.image
self.field_name = "preview_image"
elif img_choice == "image" and self._has_valid_image(lead, "image"):
self.file = lead.image
self.field_name = "image"
elif img_choice == "listing_like":
if self._has_valid_image(preview, "preview_image"):
self.file = preview.image
self.field_name = "preview_image"
elif self._has_valid_image(lead, "image"):
self.file = lead.image
self.field_name = "image"

def _set_primary_field(self):
try:
primary = IPrimaryFieldInfo(self.context, None)
if (
INamedField.providedBy(primary.field)
and hasattr(primary.value, "getSize") # noqa
and primary.value.getSize() > 0 # noqa
):
self.file = primary.value
self.field_name = primary.fieldname
except TypeError:
pass


@adapter(IEvent, IFeed)
class EventItem(DexterityItem):
class EventItem(CustomFeedItem):
@property
def startdate(self):
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,9 @@
<title tal:content="item/title" />
<link tal:content="link" />
<description tal:content="item/description" />
<tal:image tal:condition="python: item.field_name == 'image'" tal:on-error="string:">
<tal:image_metadata tal:define="url string:${link}/@@images/image/${image_miniature};
<tal:image tal:condition="python: item.field_name in {'image', 'preview_image'}" >
<tal:image_metadata tal:define="image_type python: item.field_name;
url string:${link}/@@images/${image_type}/${image_miniature};
size item/file/size|nothing;
mime item/file/contentType|nothing">
<enclosure url="${url}" tal:attributes="size size; type mime"/>
Expand Down
22 changes: 22 additions & 0 deletions src/redturtle/volto/configure.zcml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@
post_handler=".setuphandlers.uninstall"
/>

<genericsetup:registerProfile
name="profile_to_4307"
title="RedTurtle: Volto (to 4307)"
description="Installs the redturtle.volto add-on."
provides="Products.GenericSetup.interfaces.EXTENSION"
directory="profiles/to_4307"
/>

<utility
factory=".setuphandlers.HiddenProfiles"
name="redturtle.volto-hiddenprofiles"
Expand All @@ -47,4 +55,18 @@
handler=".events.manage_auth_token"
/>

<!-- provide interface to News-->
<class class="plone.volto.content.FolderishNewsItem">
<implements interface="redturtle.volto.interfaces.ICustomFeedItem" />
</class>
<!-- <class class="plone.volto.content.FolderishEvent">
<implements interface="redturtle.volto.interfaces.ICustomFeedItem" />
</class> -->
<class class="plone.volto.content.FolderishDocument">
<implements interface="redturtle.volto.interfaces.ICustomFeedItem" />
</class>
<class class="plone.app.contenttypes.content.Link">
<implements interface="redturtle.volto.interfaces.ICustomFeedItem" />
</class>

</configure>
7 changes: 7 additions & 0 deletions src/redturtle/volto/interfaces.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
from plone.dexterity.interfaces import IDexterityContent
from plone.restapi.controlpanels.interfaces import IControlpanel
from redturtle.volto import _
from zope.interface import Interface
Expand Down Expand Up @@ -40,3 +41,9 @@ class IRedTurtleVoltoSettings(Interface):
default=False,
required=False,
)


class ICustomFeedItem(IDexterityContent):
"""
Marker interface for custom feed items.
"""
2 changes: 1 addition & 1 deletion src/redturtle/volto/profiles/default/metadata.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
<version>4306</version>
<version>4307</version>
<dependencies>
<dependency>profile-plone.volto:default</dependency>
<dependency>profile-plone.app.caching:with-caching-proxy</dependency>
Expand Down
11 changes: 11 additions & 0 deletions src/redturtle/volto/profiles/default/registry/rss.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,15 @@
</field>
<value>thumb</value>
</record>
<record name="redturtle.volto.rss_image_choice">
<value>image</value>
<field type="plone.registry.field.Choice">
<title>RSS Image Choice</title>
<values>
<element>listing_like</element>
<element>image</element>
<element>preview_image</element>
</values>
</field>
</record>
</registry>
13 changes: 13 additions & 0 deletions src/redturtle/volto/profiles/to_4307/registry.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<registry>
<record name="redturtle.volto.rss_image_choice">
<value>image</value>
<field type="plone.registry.field.Choice">
<title>RSS Image Choice</title>
<values>
<element>listing_like</element>
<element>image</element>
<element>preview_image</element>
</values>
</field>
</record>
</registry>
2 changes: 1 addition & 1 deletion src/redturtle/volto/profiles/uninstall/controlpanel.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0"?>
<object name="portal_controlpanel"
xmlns:i18n="http://xml.zope.org/namespaces/i18n"
i18n:domain="design.plone.contenttypes">
i18n:domain="redturtle.volto">

<configlet
title="RedTurtle Volto Settings"
Expand Down
121 changes: 121 additions & 0 deletions src/redturtle/volto/tests/test_rss.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# -*- coding: utf-8 -*-
"""Setup tests for this package."""
from plone import api
from plone.app.testing import setRoles
from plone.app.testing import TEST_USER_ID
from redturtle.volto.adapters.rss import CustomFeedItem
from redturtle.volto.interfaces import ICustomFeedItem


try:
from plone.base.interfaces.syndication import IFeed
except ModuleNotFoundError:
from Products.CMFPlone.interfaces.syndication import IFeed

from plone.namedfile.file import NamedBlobImage
from redturtle.volto.testing import REDTURTLE_VOLTO_API_FUNCTIONAL_TESTING

import os
import unittest


class TestCustomRSSFeed(unittest.TestCase):
layer = REDTURTLE_VOLTO_API_FUNCTIONAL_TESTING

def setUp(self):
self.app = self.layer["app"]
self.portal = self.layer["portal"]
old_behavior = self.portal["portal_types"]["News Item"].behaviors
self.portal["portal_types"]["News Item"].behaviors = old_behavior + (
"volto.preview_image",
)
self.portal_url = self.portal.absolute_url()
self.feed = IFeed(self.portal)
setRoles(self.portal, TEST_USER_ID, ["Manager"])

filename = os.path.join(os.path.dirname(__file__), "logo.png")
self.news1 = api.content.create(
container=self.portal,
type="News Item",
title="News 1",
description="",
)
self.news1.image = NamedBlobImage(
data=open(filename, "rb").read(),
filename="logo.png",
contentType="image/png",
)

self.news2 = api.content.create(
container=self.portal,
type="News Item",
title="News 2",
description="",
)
self.news2.preview_image = NamedBlobImage(
data=open(filename, "rb").read(),
filename="logo.png",
contentType="image/png",
)

self.news3 = api.content.create(
container=self.portal,
type="News Item",
title="News 3",
description="",
)
self.news3.image = NamedBlobImage(
data=open(filename, "rb").read(),
filename="image.png",
contentType="image/png",
)
self.news3.preview_image = NamedBlobImage(
data=open(filename, "rb").read(),
filename="preview.png",
contentType="image/png",
)

def test_assert_news_has_volto_preview_image(self):
self.assertTrue(
"volto.preview_image" in self.portal["portal_types"]["News Item"].behaviors
)

def test_rss_item_iface_provided(self):
self.assertTrue(ICustomFeedItem.providedBy(self.news1))
self.assertTrue(ICustomFeedItem.providedBy(self.news2))

def test_rss_item_field_name_image(self):

adapter_news_1 = CustomFeedItem(self.news1, self.feed)
adapter_news_2 = CustomFeedItem(self.news2, self.feed)

# news 1 has image feld
self.assertEqual(adapter_news_1.field_name, "image")

# news 2 has the preview image field compiled, so has the default options
# is to use image field field_name will be None
self.assertEqual(adapter_news_2.field_name, None)

def test_rss_item_field_name_preview_image(self):

api.portal.set_registry_record(
"redturtle.volto.rss_image_choice", "preview_image"
)
adapter_news_1 = CustomFeedItem(self.news1, self.feed)
adapter_news_2 = CustomFeedItem(self.news2, self.feed)

self.assertEqual(adapter_news_1.field_name, None)
self.assertEqual(adapter_news_2.field_name, "preview_image")

def test_rss_item_field_name_like_listing(self):
api.portal.set_registry_record(
"redturtle.volto.rss_image_choice", "listing_like"
)
adapter_news_1 = CustomFeedItem(self.news1, self.feed)
adapter_news_2 = CustomFeedItem(self.news2, self.feed)
adapter_news_3 = CustomFeedItem(self.news3, self.feed)

self.assertEqual(adapter_news_1.field_name, "image")
self.assertEqual(adapter_news_2.field_name, "preview_image")
# image 3 has both image and preview_image, so the preview one is used
self.assertEqual(adapter_news_3.field_name, "preview_image")
7 changes: 7 additions & 0 deletions src/redturtle/volto/upgrades.py
Original file line number Diff line number Diff line change
Expand Up @@ -515,3 +515,10 @@ def to_4306(context):
installer = get_installer(portal, portal.REQUEST)
if not installer.is_product_installed("collective.volto.sitesettings"):
installer.install_product(product_id="collective.volto.sitesettings")


def to_4307(context):
context.runImportStepFromProfile(
"profile-redturtle.volto:profile_to_4307", "plone.app.registry", False
)
api.portal.set_registry_record("redturtle.volto.rss_image_choice", "image")
9 changes: 9 additions & 0 deletions src/redturtle/volto/upgrades.zcml
Original file line number Diff line number Diff line change
Expand Up @@ -240,4 +240,13 @@
destination="4306"
handler=".upgrades.to_4306"
/>
<genericsetup:upgradeStep
title="Add new field in registry to select the miniature field for RSS feed"
description=""
profile="redturtle.volto:default"
source="4306"
destination="4307"
handler=".upgrades.to_4307"
/>

</configure>

0 comments on commit 2c6b6a1

Please sign in to comment.