diff --git a/fields/ref.py b/fields/ref.py
index 5318d94..289a626 100644
--- a/fields/ref.py
+++ b/fields/ref.py
@@ -226,9 +226,15 @@ class Ref(Field):
''')
- # PX that displays referred objects as a list.
+ pxToggleIcon = Px('''
+ ''')
+
+ # PX that displays referred objects as a list
pxViewList = Px('''
+ :field.pxToggleIcon
:_(subLabel)
(:totalNumber)
:field.pxAdd
@@ -533,7 +539,7 @@ class Ref(Field):
scolspan=1, swidth=None, sheight=None, sselect=None,
persist=True, render='list', menuIdMethod=None,
menuInfoMethod=None, menuUrlMethod=None, view=None, xml=None,
- showActions=True):
+ showActions=True, collapsible=False):
self.klass = klass
self.attribute = attribute
# May the user add new objects through this ref ? "add" may also contain
@@ -720,6 +726,9 @@ class Ref(Field):
# appear in a "div" tag, below the object title; if "inline", they will
# appear besides it, producing a more compact list of results.
self.showActions = showActions
+ # If "collapsible" is True, a "+/-" icon will allow to expand/collapse
+ # the tied or available objects.
+ self.collapsible = collapsible
if showActions == True: self.showActions = 'block'
# Call the base constructor
Field.__init__(self, validator, multiplicity, default, show, page,
diff --git a/gen/mixins/ToolMixin.py b/gen/mixins/ToolMixin.py
index e776a83..65c24b2 100644
--- a/gen/mixins/ToolMixin.py
+++ b/gen/mixins/ToolMixin.py
@@ -927,8 +927,10 @@ class ToolMixin(BaseMixin):
cfg = self.getProductConfig(True).ldap
if cfg: user = cfg.getUser(self.appy(), login, password)
elif source == 'any':
- # Get the user object, be it really local or a copy of a LDAP user
- user = self.getUser(source='zodb') or self.getUser(source='ldap')
+ # Get the User object, be it really local or representing an
+ # external user. This way, we avoid contacting the distant source
+ # every time authentification is required.
+ user = tool.search1('User', noSecurity=True, login=login)
if not user: return
# Authentify the user if required
if authentify:
@@ -943,6 +945,7 @@ class ToolMixin(BaseMixin):
gutils.writeCookie(login, password, req)
# Cache the user and some precomputed values, for performance
req.user = user
+ req.userLogin = user.login
req.userRoles = user.getRoles()
req.userLogins = user.getLogins()
req.zopeUser = user.getZopeUser()
@@ -958,7 +961,8 @@ class ToolMixin(BaseMixin):
msg = self.translate('enable_cookies')
return self.goto(urlBack, msg)
# Authenticate the user
- if self.getUser(authentify=True, source='any'):
+ if self.getUser(authentify=True, source='zodb') or \
+ self.getUser(authentify=True, source='ldap'):
msg = self.translate('login_ok')
logMsg = 'logged in.'
else:
@@ -1015,6 +1019,10 @@ class ToolMixin(BaseMixin):
id = a.getId()
if id and (os.path.splitext(id)[-1].lower() in tool.staticExtensions):
return self._nobody.__of__(self)
+ # Skip authorization when the performing http login: else, it will be
+ # done twice.
+ if (id == 'config') and (v.__name__ == 'performLogin'):
+ return self._nobody.__of__(self)
# Identify and authentify the user
user = tool.getUser(authentify=True, source='any')
if not user:
diff --git a/gen/ui/appy.css b/gen/ui/appy.css
index db93853..374204b 100644
--- a/gen/ui/appy.css
+++ b/gen/ui/appy.css
@@ -34,7 +34,7 @@ select { border: 1px solid #d0d0d0; background-color: white }
textarea { width: 99%; font: 100% "Lucida Grande","Lucida Sans Unicode",Helvetica,Arial,Verdana,sans-serif;
border: 1px solid #d0d0d0; background-color: white }
-label { color: #888888; font-size: 11px; margin: 3px 0;
+label { color: #555555; font-size: 11px; margin: 3px 0;
text-transform: uppercase }
legend { padding-bottom: 2px; padding-right: 3px; color: black }
ul { line-height: 1.2em; margin: 0 0 0.2em 0.6em; padding: 0;
@@ -69,7 +69,8 @@ input.buttonFixed { width:110px; padding: 0 0 0 10px }
.xhtml p { margin: 3px 0 7px 0 }
.clickable { cursor: pointer }
.help { cursor: help }
-.refLink { font-style: italic; padding-left: 5px; font-size: 90%; color: grey }
+.refLink { font-style: italic; padding-left: 5px; font-size: 90%;
+ color: #555555 }
.buttons { margin-left: 4px }
.objectButtons { margin-top: 5px }
.message { position: absolute; top: -40px; left: 50%; font-size: 90%;
@@ -82,7 +83,7 @@ input.buttonFixed { width:110px; padding: 0 0 0 10px }
background-color: #d7dee4; border-radius: 2px 2px 2px 2px;
box-shadow: 0 2px 4px #A9A9A9 }
.focus td { padding: 4px 0px 4px 4px }
-.discreet { font-size: 90%; color: grey }
+.discreet { font-size: 90%; color: #555555 }
.title {}
.lostPassword { font-size: 90%; color: white; padding-left: 1em }
.current { font-weight: bold }
@@ -115,7 +116,7 @@ td.search { padding-top: 8px }
.addFormMenu { display: inline; padding: 0 5px 0 3px }
.inline { display: inline }
.list { margin-bottom: 3px }
-.list td, .list th { border: 3px solid #ededed; color: grey;
+.list td, .list th { border: 3px solid #ededed; color: #555555;
padding: 3px 5px 3px 5px }
.list th { background-color: #e5e5e5; font-style: italic; font-weight: normal }
.compact { font-size: 90%; width: 100% }
@@ -125,7 +126,7 @@ td.search { padding-top: 8px }
.grid td { padding: 3px 3px 0 3px }
.msgTable { margin: 6px 0; width: 100%; font-size: 93% }
.msgTable tr { vertical-align: top }
-.msgTable td, .msgTable th { border: 1px solid grey; color: grey;
+.msgTable td, .msgTable th { border: 1px solid grey; color: #555555;
padding: 0px 5px; text-align: left }
.msgTable th { background-color: #f0e3b8; font-style: italic;
font-weight: normal }
@@ -184,7 +185,7 @@ td.search { padding-top: 8px }
.tabs { position:relative; bottom:-2px }
.tab { padding: 0 10px 0 10px; text-align: center; font-size: 90%;
font-weight: bold}
-.language {color: grey; font-size: 7pt; border: grey 1px solid; padding: 2px;
+.language {color: #555555; font-size: 7pt; border: grey 1px solid; padding: 2px;
margin: 0 2px 0 4px; font-family: monospace }
.highlight { background-color: yellow }
.globalActions { margin-bottom: 4px }
diff --git a/gen/wrappers/UserWrapper.py b/gen/wrappers/UserWrapper.py
index 9c30ed4..6b4254f 100644
--- a/gen/wrappers/UserWrapper.py
+++ b/gen/wrappers/UserWrapper.py
@@ -121,7 +121,8 @@ class UserWrapper(AbstractWrapper):
zopeUser = self.getZopeUser()
tool = self.tool.o
zopeUser.__ = self.encryptPassword(newPassword)
- if self.user and (self.user.login == login):
+ req = tool.REQUEST
+ if hasattr(req, 'user') and (req.userLogin == login):
# The user for which we change the password is the currently logged
# user. So update the authentication cookie, too.
gutils.writeCookie(login, newPassword, self.request)
@@ -302,7 +303,7 @@ class UserWrapper(AbstractWrapper):
# Try first to get those logins from a cache on the request, if this
# user corresponds to the logged user.
rq = self.request
- if (self.user == self) and hasattr(rq, 'userLogins'):
+ if hasattr(rq, 'userLogins') and (rq.userLogin == self.login):
return rq.userLogins
# Compute it
res = [group.login for group in self.groups]
@@ -316,9 +317,9 @@ class UserWrapper(AbstractWrapper):
# Try first to get those roles from a cache on the request, if this user
# corresponds to the logged user.
rq = self.request
- if (self.user == self) and hasattr(rq, 'userRoles'):
+ if hasattr(rq, 'userRoles') and (rq.userLogin == self.login):
return rq.userRoles
- # Compute it.
+ # Compute it
res = list(self.roles)
# Add ungrantable roles
if self.o.id == 'anon':