Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions news/3953.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Move Classic-UI template/view code to plone.app.layout
[tlotze]
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ Zope = [
'zope.sendmail', 'Zope'
]
'plone.base' = [
'plone.batching', 'plone.registry', 'plone.schema','plone.z3cform',
'plone.batching', 'plone.registry', 'plone.schema',
'Products.CMFCore', 'Products.CMFDynamicViewFTI',
]
python-dateutil = ['dateutil']
Expand Down
5 changes: 0 additions & 5 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,26 +56,21 @@
"Pillow",
"Products.GenericSetup",
"Products.PlonePAS >= 5.0.1",
"Products.statusmessages",
"Zope",
"plone.autoform >= 1.2",
"plone.app.event",
"plone.app.vocabularies",
"plone.base",
"plone.formwidget.namedfile >= 1.0.3",
"plone.i18n",
"plone.memoize",
"plone.namedfile",
"plone.protect",
"plone.registry",
"plone.schema",
"plone.schemaeditor",
"plone.supermodel",
"plone.uuid",
"plone.z3cform",
"z3c.form",
"zope.annotation",
"zope.cachedescriptors",
"zope.component",
"zope.event",
"zope.interface",
Expand Down
9 changes: 9 additions & 0 deletions src/plone/app/users/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
jbot_deprecations = {
"plone.app.users.browser.account-configlet.pt": "plone.app.layout.users.account-configlet.pt",
"plone.app.users.browser.account-panel.pt": "plone.app.layout.users.account-panel.pt",
"plone.app.users.browser.memberregistration.pt": "plone.app.layout.users.memberregistration.pt",
"plone.app.users.browser.newuser_form.pt": "plone.app.layout.users.newuser_form.pt",
"plone.app.users.browser.registered.pt": "plone.app.layout.users.registered.pt",
"plone.app.users.browser.register_form.pt": "plone.app.layout.users.register_form.pt",
"plone.app.users.browser.schema_layout.pt": "plone.app.layout.users.schema_layout.pt",
}
61 changes: 0 additions & 61 deletions src/plone/app/users/browser/account-configlet.pt

This file was deleted.

46 changes: 0 additions & 46 deletions src/plone/app/users/browser/account-panel.pt

This file was deleted.

191 changes: 13 additions & 178 deletions src/plone/app/users/browser/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,29 @@
from PIL import UnidentifiedImageError
from plone.app.users.browser.interfaces import IAccountPanelForm
from plone.app.users.browser.schemaeditor import getFromBaseSchema
from plone.app.users.utils import notifyWidgetActionExecutionError
from plone.autoform.form import AutoExtensibleForm
from plone.base import PloneMessageFactory as _
from plone.base.interfaces import INavigationRoot
from plone.base.interfaces import IPloneSiteRoot
from plone.base.interfaces import ISecuritySchema
from plone.base.utils import safe_text
from plone.namedfile.file import NamedBlobImage
from plone.protect import CheckAuthenticator
from plone.registry.interfaces import IRegistry
from Products.CMFCore.utils import getToolByName
from Products.CMFPlone.controlpanel.events import ConfigurationChangedEvent
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
from Products.PlonePAS.tools.membership import default_portrait
from Products.statusmessages.interfaces import IStatusMessage
from z3c.form import button
from z3c.form import form
from z3c.form.interfaces import NOT_CHANGED
from zope import schema
from zope.cachedescriptors.property import Lazy as lazy_property
from zope.component import getMultiAdapter
from zope.component import getUtility
from zope.component import provideAdapter
from zope.event import notify
from zope.globalrequest import getRequest
from zope.interface import implementer
from ZTUtils import make_query

import zope.deferredimport

zope.deferredimport.initialize()

zope.deferredimport.deprecated(
"Please use from plone.app.layout.users.account import AccountPanelForm instead.",
AccountPanelForm="plone.app.layout.users.account:AccountPanelForm",
)

MESSAGE_EMAIL_CANNOT_CHANGE = _(
"message_email_cannot_change",
Expand Down Expand Up @@ -199,70 +195,10 @@ def timezone(self, value):
return self._setProperty("timezone", value)


@implementer(IAccountPanelForm)
class AccountPanelForm(AutoExtensibleForm, form.Form):
"""A simple form to be used as a basis for account panel screens."""

schema = IAccountPanelForm
template = ViewPageTemplateFile("account-panel.pt")
enableCSRFProtection = True

hidden_widgets = []
successMessage = _("Changes saved.")
noChangesMessage = _("No changes made.")

@lazy_property
def member(self):
mtool = getToolByName(self.context, "portal_membership")
if self.request.get("userid"):
return mtool.getMemberById(self.request.get("userid"))
return mtool.getAuthenticatedMember()

@property
def label(self):
return self.member.getProperty("fullname") or self.member.getUserName()

def _differentEmail(self, email):
"""Check if the submitted form email address differs from the existing
one.
class AccountPanelValidation:

Keeping your email the same (which happens when you change something
else on the personalize form) or changing it back to your login name,
is fine.

So: we only return True if it is *really* a different email.
"""
membership = getToolByName(self.context, "portal_membership")
if self.request.get("userid"):
member = membership.getMemberById(self.request.get("userid"))
else:
member = membership.getAuthenticatedMember()
if email in (member.getId(), member.getUserName()):
return False
# By default, PAS transforms login names to lowercase, at least when
# email-as-login is used. So compare the transformed/normalized names.
pas = getToolByName(self.context, "acl_users")
email_normalized = pas.applyTransform(email)
# The user name should already have been normalized, but let's make sure.
login_normalized = pas.applyTransform(member.getUserName())
return email_normalized != login_normalized

def makeQuery(self):
userid = self.request.form.get("userid", None)
if userid is not None:
return "?{}".format(make_query({"userid": userid}))
return ""

def action(self):
return self.request.getURL() + self.makeQuery()

def validate_email(self, action, data):
def validate_email(self, data):
context = aq_inner(self.context)
error_keys = [error.field.getName() for error in action.form.widgets.errors]
if "email" in error_keys:
# There is already a validation error for email,
# so there is no need for further validation.
return
# We only need an extra check if email is used as login.
registry = getUtility(IRegistry)
security_settings = registry.forInterface(ISecuritySchema, prefix="plone")
Expand Down Expand Up @@ -291,121 +227,20 @@ def validate_email(self, action, data):
# Only allow if unchanged: then that member is us!
if self._differentEmail(email):
err_str = MESSAGE_EMAIL_IN_USE
if err_str:
notifyWidgetActionExecutionError(action, "email", err_str)
return 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" in error_keys:
return
portrait_file = data["portrait"]
if portrait_file is None or portrait_file is NOT_CHANGED:
return
with portrait_file.open() as portrait:
try:
Image.open(portrait)
except UnidentifiedImageError:
notifyWidgetActionExecutionError(
action, "portrait", MESSAGE_IMAGE_NOT_SUPPORTED
)
return MESSAGE_IMAGE_NOT_SUPPORTED
except Exception as exc:
raise exc

@button.buttonAndHandler(_("Save"))
def handleSave(self, action):
CheckAuthenticator(self.request)
data, errors = self.extractData()

# Extra validation for email, when it is there. email is not in the
# data when you are at the personal-preferences page.
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
if self.applyChanges(data):
IStatusMessage(self.request).addStatusMessage(
self.successMessage, type="info"
)
notify(ConfigurationChangedEvent(self, data))
self._on_save(data)
else:
IStatusMessage(self.request).addStatusMessage(
self.noChangesMessage, type="info"
)
self.request.response.redirect(self.action())

def updateActions(self):
super().updateActions()
if self.actions and "save" in self.actions:
self.actions["save"].addClass("btn btn-primary")

@button.buttonAndHandler(_("Cancel"))
def cancel(self, action):
IStatusMessage(self.request).addStatusMessage(
_("Changes canceled."), type="info"
)
self.request.response.redirect(
"{}{}".format(self.request["ACTUAL_URL"], self.makeQuery())
)

def _on_save(self, data=None):
pass

def prepareObjectTabs(self, default_tab="view", sort_first=["folderContents"]):
context = self.context
mt = getToolByName(context, "portal_membership")
tabs = []
navigation_root_url = context.absolute_url()

def _check_allowed(context, request, name):
"""Check, if user has required permissions on view."""
view = getMultiAdapter((context, request), name=name)
allowed = True
for perm in view.__ac_permissions__:
allowed = allowed and mt.checkPermission(perm[0], context)
return allowed

if _check_allowed(context, self.request, "personal-information"):
tabs.append(
{
"title": _(
"title_personal_information_form", "Personal Information"
),
"url": navigation_root_url + "/@@personal-information",
"selected": (self.__name__ == "personal-information"),
"id": "user_data-personal-information",
}
)

if _check_allowed(context, self.request, "personal-preferences"):
tabs.append(
{
"title": _("Personal Preferences"),
"url": navigation_root_url + "/@@personal-preferences",
"selected": (self.__name__ == "personal-preferences"),
"id": "user_data-personal-preferences",
}
)

member = mt.getAuthenticatedMember()
if member.canPasswordSet():
tabs.append(
{
"title": _("label_password", "Password"),
"url": navigation_root_url + "/@@change-password",
"selected": (self.__name__ == "change-password"),
"id": "user_data-change-password",
}
)
return tabs
Loading
Loading