Skip to content

Commit

Permalink
Merge pull request #123 from plone/122-portrait-image-file-not-suppor…
Browse files Browse the repository at this point in the history
…ted-by-pil

122 portrait image file not supported by pil
  • Loading branch information
mauritsvanrees authored Apr 22, 2024
2 parents 8bb440e + 0863c45 commit ee9ebe9
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 0 deletions.
4 changes: 4 additions & 0 deletions news/122.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Fix #122 validating if image is supported by PIL showing a validation error if not.
Include Pillow dependency in setup.py.
Fix ValueError: User could not be found in BaseTest setUp adding a transaction.commit().
[rber474]
29 changes: 29 additions & 0 deletions plone/app/users/browser/account.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from AccessControl import Unauthorized
from Acquisition import aq_inner
from PIL import Image
from PIL import UnidentifiedImageError
from plone.app.layout.navigation.interfaces import INavigationRoot
from plone.app.users.browser.interfaces import IAccountPanelForm
from plone.app.users.browser.schemaeditor import getFromBaseSchema
Expand Down Expand Up @@ -44,6 +46,11 @@
),
)

MESSAGE_IMAGE_NOT_SUPPORTED = _(
"message_image_not_supported",
"The file you selected is not supported by Pillow. " "Please choose another.",
)


def getSchema(schema_interface, schema_adapter, form_name=None):
request = getRequest()
Expand Down Expand Up @@ -259,6 +266,23 @@ def validate_email(self, action, data):
if err_str:
notifyWidgetActionExecutionError(action, "email", err_str)

def validate_portrait(self, action, data):
"""Portrait validation.
Checks if image is supported by Pillow.
SVG files are not yet supported.
"""
error_keys = [error.field.getName() for error in action.form.widgets.errors]
if "portrait" not in error_keys and data["portrait"] is not None:
portrait = data["portrait"].open()
try:
Image.open(portrait)
except UnidentifiedImageError:
notifyWidgetActionExecutionError(
action, "portrait", MESSAGE_IMAGE_NOT_SUPPORTED
)
except Exception as exc:
raise exc

@button.buttonAndHandler(_("Save"))
def handleSave(self, action):
CheckAuthenticator(self.request)
Expand All @@ -269,6 +293,11 @@ def handleSave(self, action):
if "email" in data:
self.validate_email(action, data)

# Validate portrait, upload image could be not supported
# by PIL what raises an exception when scaling image.
if "portrait" in data:
self.validate_portrait(action, data)

if action.form.widgets.errors:
self.status = self.formErrorsMessage
return
Expand Down
2 changes: 2 additions & 0 deletions plone/app/users/tests/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from zope.component import getSiteManager
from zope.component import getUtility

import transaction
import unittest


Expand All @@ -43,6 +44,7 @@ def setUp(self):

self.browser = Browser(self.layer["app"])
self.request = self.layer["request"]
transaction.commit()

def tearDown(self):
login(self.portal, "admin")
Expand Down
45 changes: 45 additions & 0 deletions plone/app/users/tests/test_portrait.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from pkg_resources import resource_stream
from plone.app.testing import SITE_OWNER_NAME
from plone.app.testing import SITE_OWNER_PASSWORD
from plone.app.users.tests.base import BaseTestCase


class TestPortrait(BaseTestCase):
def test_regression_supported_image_type_122(self):
# https://github.com/plone/plone.app.users/issues/122

self.browser.open("http://nohost/plone/")
self.browser.getLink("Log in").click()
self.browser.getControl("Login Name").value = SITE_OWNER_NAME
self.browser.getControl("Password").value = SITE_OWNER_PASSWORD
self.browser.getControl("Log in").click()
self.browser.open("http://nohost/plone/@@personal-information")
self.browser.getControl(name="form.widgets.email").value = "[email protected]"
portrait_file = resource_stream("plone.app.users.tests", "onepixel.jpg")
self.browser.getControl(name="form.widgets.portrait").add_file(
portrait_file, "image/jpg", "onepixel.# jpg"
)
self.browser.getControl("Save").click()
self.assertIn("Changes saved.", self.browser.contents)

def test_not_supported_image_type_122(self):
# https://github.com/plone/plone.app.users/issues/122

self.browser.open("http://nohost/plone/")
self.browser.getLink("Log in").click()
self.browser.getControl("Login Name").value = SITE_OWNER_NAME
self.browser.getControl("Password").value = SITE_OWNER_PASSWORD
self.browser.getControl("Log in").click()
self.browser.open("http://nohost/plone/@@personal-information")
self.browser.getControl(name="form.widgets.email").value = "[email protected]"
portrait_file = resource_stream(
"plone.app.users.tests", "transparent_square.svg"
)
self.browser.getControl(name="form.widgets.portrait").add_file(
portrait_file, "image/svg+xml", "onepixel.# jpg"
)
self.browser.getControl("Save").click()
self.assertIn(
"The file you selected is not supported by Pillow. Please choose another.",
self.browser.contents,
)
3 changes: 3 additions & 0 deletions plone/app/users/tests/transparent_square.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ Zope = [
]
python-dateutil = ['dateutil']
ignore-packages = ['Products.CMFPlone']
Pillow = ['PIL']

##
# Add extra configuration options in .meta.toml:
Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
extras_require=extras_require,
install_requires=[
"Acquisition",
"Pillow",
"Products.GenericSetup",
"Products.PlonePAS >= 5.0.1",
"Products.statusmessages",
Expand Down

0 comments on commit ee9ebe9

Please sign in to comment.